summaryrefslogtreecommitdiffstats
path: root/src/gui
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui')
-rw-r--r--src/gui/accessible/qaccessible.cpp4
-rw-r--r--src/gui/accessible/qaccessiblecache_p.h2
-rw-r--r--src/gui/configure.json78
-rw-r--r--src/gui/doc/snippets/textdocumentendsnippet.cpp2
-rw-r--r--src/gui/gui.pro1
-rw-r--r--src/gui/image/qbitmap.h2
-rw-r--r--src/gui/image/qicon.cpp4
-rw-r--r--src/gui/image/qicon.h12
-rw-r--r--src/gui/image/qiconengine.h2
-rw-r--r--src/gui/image/qimage.cpp148
-rw-r--r--src/gui/image/qimage.h32
-rw-r--r--src/gui/image/qimage_conversions.cpp4
-rw-r--r--src/gui/image/qimage_p.h6
-rw-r--r--src/gui/image/qimagereader.cpp2
-rw-r--r--src/gui/image/qmovie.cpp3
-rw-r--r--src/gui/image/qpicture.cpp1
-rw-r--r--src/gui/image/qpicture.h6
-rw-r--r--src/gui/image/qpixmap.cpp2
-rw-r--r--src/gui/image/qpixmap.h8
-rw-r--r--src/gui/image/qpixmapcache.cpp2
-rw-r--r--src/gui/image/qpixmapcache.h10
-rw-r--r--src/gui/image/qpixmapcache_p.h2
-rw-r--r--src/gui/image/qpnghandler.cpp72
-rw-r--r--src/gui/image/qppmhandler_p.h2
-rw-r--r--src/gui/image/qxbmhandler.cpp1
-rw-r--r--src/gui/image/qxpmhandler.cpp11
-rw-r--r--src/gui/itemmodels/qstandarditemmodel_p.h12
-rw-r--r--src/gui/kernel/kernel.pri2
-rw-r--r--src/gui/kernel/qcursor.cpp2
-rw-r--r--src/gui/kernel/qcursor.h14
-rw-r--r--src/gui/kernel/qdnd_p.h6
-rw-r--r--src/gui/kernel/qevent.cpp18
-rw-r--r--src/gui/kernel/qevent.h22
-rw-r--r--src/gui/kernel/qguiapplication.cpp86
-rw-r--r--src/gui/kernel/qguiapplication_p.h20
-rw-r--r--src/gui/kernel/qhighdpiscaling_p.h2
-rw-r--r--src/gui/kernel/qinputdevicemanager.cpp4
-rw-r--r--src/gui/kernel/qinputdevicemanager_p.h4
-rw-r--r--src/gui/kernel/qinputdevicemanager_p_p.h5
-rw-r--r--src/gui/kernel/qinputmethod_p.h2
-rw-r--r--src/gui/kernel/qkeysequence.cpp5
-rw-r--r--src/gui/kernel/qkeysequence.h10
-rw-r--r--src/gui/kernel/qopenglcontext_p.h22
-rw-r--r--src/gui/kernel/qpalette.cpp4
-rw-r--r--src/gui/kernel/qpalette.h8
-rw-r--r--src/gui/kernel/qpixelformat.h64
-rw-r--r--src/gui/kernel/qplatformdialoghelper.cpp6
-rw-r--r--src/gui/kernel/qplatformdialoghelper.h4
-rw-r--r--src/gui/kernel/qplatformintegration.h2
-rw-r--r--src/gui/kernel/qplatformtheme.h2
-rw-r--r--src/gui/kernel/qscreen.cpp2
-rw-r--r--src/gui/kernel/qscreen_p.h18
-rw-r--r--src/gui/kernel/qsessionmanager.cpp6
-rw-r--r--src/gui/kernel/qshapedpixmapdndwindow_p.h2
-rw-r--r--src/gui/kernel/qsimpledrag.cpp1
-rw-r--r--src/gui/kernel/qwindow.cpp9
-rw-r--r--src/gui/kernel/qwindow.h4
-rw-r--r--src/gui/kernel/qwindow_p.h90
-rw-r--r--src/gui/kernel/qwindowsysteminterface.cpp4
-rw-r--r--src/gui/kernel/qwindowsysteminterface.h4
-rw-r--r--src/gui/kernel/qwindowsysteminterface_p.h10
-rw-r--r--src/gui/math3d/qgenericmatrix.h4
-rw-r--r--src/gui/math3d/qmatrix4x4.cpp10
-rw-r--r--src/gui/opengl/qopengl.cpp4
-rw-r--r--src/gui/opengl/qopengldebug.h6
-rw-r--r--src/gui/opengl/qopenglengineshadermanager_p.h2
-rw-r--r--src/gui/opengl/qopengles2ext.h442
-rw-r--r--src/gui/opengl/qopenglext.h555
-rw-r--r--src/gui/opengl/qopenglextensions_p.h2
-rw-r--r--src/gui/opengl/qopenglframebufferobject_p.h8
-rw-r--r--src/gui/opengl/qopenglgradientcache_p.h2
-rw-r--r--src/gui/opengl/qopenglpaintengine_p.h8
-rw-r--r--src/gui/opengl/qopenglpixeltransferoptions.h6
-rw-r--r--src/gui/opengl/qopenglqueryhelper_p.h24
-rw-r--r--src/gui/opengl/qopengltexture.cpp263
-rw-r--r--src/gui/opengl/qopengltexture.h26
-rw-r--r--src/gui/opengl/qopengltexture_p.h4
-rw-r--r--src/gui/opengl/qopengltextureglyphcache_p.h2
-rw-r--r--src/gui/opengl/qopengltexturehelper_p.h18
-rw-r--r--src/gui/opengl/qopenglversionfunctions.h2
-rw-r--r--src/gui/painting/painting.pri16
-rw-r--r--src/gui/painting/qblendfunctions_p.h16
-rw-r--r--src/gui/painting/qbrush.cpp47
-rw-r--r--src/gui/painting/qbrush.h11
-rw-r--r--src/gui/painting/qcolor.cpp357
-rw-r--r--src/gui/painting/qcolor.h186
-rw-r--r--src/gui/painting/qcolormatrix_p.h249
-rw-r--r--src/gui/painting/qcolorspace.cpp744
-rw-r--r--src/gui/painting/qcolorspace.h139
-rw-r--r--src/gui/painting/qcolorspace_p.h138
-rw-r--r--src/gui/painting/qcolortransferfunction_p.h207
-rw-r--r--src/gui/painting/qcolortransfertable_p.h245
-rw-r--r--src/gui/painting/qcolortransform.cpp703
-rw-r--r--src/gui/painting/qcolortransform.h93
-rw-r--r--src/gui/painting/qcolortransform_p.h89
-rw-r--r--src/gui/painting/qcolortrc_p.h148
-rw-r--r--src/gui/painting/qcolortrclut.cpp (renamed from src/gui/painting/qcolorprofile.cpp)41
-rw-r--r--src/gui/painting/qcolortrclut_p.h (renamed from src/gui/painting/qcolorprofile_p.h)72
-rw-r--r--src/gui/painting/qcompositionfunctions.cpp702
-rw-r--r--src/gui/painting/qcosmeticstroker_p.h4
-rw-r--r--src/gui/painting/qdatabuffer_p.h4
-rw-r--r--src/gui/painting/qdrawhelper.cpp438
-rw-r--r--src/gui/painting/qdrawhelper_avx2.cpp6
-rw-r--r--src/gui/painting/qdrawhelper_p.h6
-rw-r--r--src/gui/painting/qdrawhelper_sse4.cpp2
-rw-r--r--src/gui/painting/qicc.cpp669
-rw-r--r--src/gui/painting/qicc_p.h70
-rw-r--r--src/gui/painting/qimagescale.cpp7
-rw-r--r--src/gui/painting/qmatrix.cpp6
-rw-r--r--src/gui/painting/qmatrix.h10
-rw-r--r--src/gui/painting/qoutlinemapper.cpp5
-rw-r--r--src/gui/painting/qoutlinemapper_p.h4
-rw-r--r--src/gui/painting/qpagelayout.h6
-rw-r--r--src/gui/painting/qpagesize.h6
-rw-r--r--src/gui/painting/qpaintdevice.cpp2
-rw-r--r--src/gui/painting/qpaintdevice.h2
-rw-r--r--src/gui/painting/qpaintengine_p.h6
-rw-r--r--src/gui/painting/qpaintengine_raster.cpp88
-rw-r--r--src/gui/painting/qpaintengine_raster_p.h22
-rw-r--r--src/gui/painting/qpaintengineex.cpp2
-rw-r--r--src/gui/painting/qpainter_p.h8
-rw-r--r--src/gui/painting/qpainterpath.cpp11
-rw-r--r--src/gui/painting/qpainterpath.h8
-rw-r--r--src/gui/painting/qpainterpath_p.h44
-rw-r--r--src/gui/painting/qpathclipper_p.h12
-rw-r--r--src/gui/painting/qpdf.cpp12
-rw-r--r--src/gui/painting/qpdfwriter.cpp10
-rw-r--r--src/gui/painting/qpdfwriter.h6
-rw-r--r--src/gui/painting/qpen.cpp4
-rw-r--r--src/gui/painting/qpen.h12
-rw-r--r--src/gui/painting/qplatformbackingstore.h4
-rw-r--r--src/gui/painting/qpolygon.h22
-rw-r--r--src/gui/painting/qrbtree_p.h14
-rw-r--r--src/gui/painting/qregion.cpp12
-rw-r--r--src/gui/painting/qregion.h28
-rw-r--r--src/gui/painting/qstroker.cpp11
-rw-r--r--src/gui/painting/qtextureglyphcache.cpp12
-rw-r--r--src/gui/painting/qtextureglyphcache_p.h2
-rw-r--r--src/gui/painting/qtransform.cpp12
-rw-r--r--src/gui/painting/qtransform.h14
-rw-r--r--src/gui/painting/qtriangulator_p.h27
-rw-r--r--src/gui/painting/qvectorpath_p.h8
-rw-r--r--src/gui/rhi/qrhi.cpp4891
-rw-r--r--src/gui/rhi/qrhi_p.h1375
-rw-r--r--src/gui/rhi/qrhi_p_p.h544
-rw-r--r--src/gui/rhi/qrhid3d11.cpp3388
-rw-r--r--src/gui/rhi/qrhid3d11_p.h76
-rw-r--r--src/gui/rhi/qrhid3d11_p_p.h624
-rw-r--r--src/gui/rhi/qrhigles2.cpp2975
-rw-r--r--src/gui/rhi/qrhigles2_p.h84
-rw-r--r--src/gui/rhi/qrhigles2_p_p.h695
-rw-r--r--src/gui/rhi/qrhimetal.mm3249
-rw-r--r--src/gui/rhi/qrhimetal_p.h80
-rw-r--r--src/gui/rhi/qrhimetal_p_p.h410
-rw-r--r--src/gui/rhi/qrhinull.cpp709
-rw-r--r--src/gui/rhi/qrhinull_p.h69
-rw-r--r--src/gui/rhi/qrhinull_p_p.h279
-rw-r--r--src/gui/rhi/qrhiprofiler.cpp603
-rw-r--r--src/gui/rhi/qrhiprofiler_p.h120
-rw-r--r--src/gui/rhi/qrhiprofiler_p_p.h121
-rw-r--r--src/gui/rhi/qrhivulkan.cpp5681
-rw-r--r--src/gui/rhi/qrhivulkan_p.h85
-rw-r--r--src/gui/rhi/qrhivulkan_p_p.h859
-rw-r--r--src/gui/rhi/qrhivulkanext_p.h81
-rw-r--r--src/gui/rhi/qshader.cpp586
-rw-r--r--src/gui/rhi/qshader_p.h227
-rw-r--r--src/gui/rhi/qshader_p_p.h84
-rw-r--r--src/gui/rhi/qshaderdescription.cpp1088
-rw-r--r--src/gui/rhi/qshaderdescription_p.h278
-rw-r--r--src/gui/rhi/qshaderdescription_p_p.h95
-rw-r--r--src/gui/rhi/rhi.pri57
-rw-r--r--src/gui/text/qabstracttextdocumentlayout.cpp31
-rw-r--r--src/gui/text/qabstracttextdocumentlayout.h1
-rw-r--r--src/gui/text/qabstracttextdocumentlayout_p.h6
-rw-r--r--src/gui/text/qcssparser_p.h12
-rw-r--r--src/gui/text/qdistancefield.cpp3
-rw-r--r--src/gui/text/qdistancefield_p.h3
-rw-r--r--src/gui/text/qfont.cpp101
-rw-r--r--src/gui/text/qfont.h12
-rw-r--r--src/gui/text/qfont_p.h31
-rw-r--r--src/gui/text/qfontdatabase.cpp13
-rw-r--r--src/gui/text/qfontengine.cpp12
-rw-r--r--src/gui/text/qfontengine_p.h29
-rw-r--r--src/gui/text/qfontengine_qpf2.cpp6
-rw-r--r--src/gui/text/qfontmetrics.h12
-rw-r--r--src/gui/text/qfragmentmap_p.h2
-rw-r--r--src/gui/text/qglyphrun.h6
-rw-r--r--src/gui/text/qglyphrun_p.h2
-rw-r--r--src/gui/text/qplatformfontdatabase.cpp11
-rw-r--r--src/gui/text/qplatformfontdatabase.h2
-rw-r--r--src/gui/text/qrawfont.cpp2
-rw-r--r--src/gui/text/qrawfont.h8
-rw-r--r--src/gui/text/qrawfont_p.h22
-rw-r--r--src/gui/text/qstatictext.h6
-rw-r--r--src/gui/text/qstatictext_p.h2
-rw-r--r--src/gui/text/qsyntaxhighlighter.cpp7
-rw-r--r--src/gui/text/qtextcursor.h6
-rw-r--r--src/gui/text/qtextdocument.cpp71
-rw-r--r--src/gui/text/qtextdocument.h22
-rw-r--r--src/gui/text/qtextdocument_p.cpp18
-rw-r--r--src/gui/text/qtextdocument_p.h6
-rw-r--r--src/gui/text/qtextdocumentfragment.cpp4
-rw-r--r--src/gui/text/qtextdocumentfragment_p.h6
-rw-r--r--src/gui/text/qtextdocumentlayout.cpp58
-rw-r--r--src/gui/text/qtextdocumentwriter.cpp19
-rw-r--r--src/gui/text/qtextengine.cpp12
-rw-r--r--src/gui/text/qtextengine_p.h18
-rw-r--r--src/gui/text/qtextformat.cpp64
-rw-r--r--src/gui/text/qtextformat.h17
-rw-r--r--src/gui/text/qtexthtmlparser.cpp9
-rw-r--r--src/gui/text/qtexthtmlparser_p.h1
-rw-r--r--src/gui/text/qtextimagehandler_p.h2
-rw-r--r--src/gui/text/qtextmarkdownimporter.cpp568
-rw-r--r--src/gui/text/qtextmarkdownimporter_p.h143
-rw-r--r--src/gui/text/qtextmarkdownwriter.cpp559
-rw-r--r--src/gui/text/qtextmarkdownwriter_p.h95
-rw-r--r--src/gui/text/qtextobject.cpp4
-rw-r--r--src/gui/text/qtextobject.h8
-rw-r--r--src/gui/text/qtextobject_p.h2
-rw-r--r--src/gui/text/qtextodfwriter.cpp6
-rw-r--r--src/gui/text/qtexttable_p.h2
-rw-r--r--src/gui/text/qzipreader_p.h4
-rw-r--r--src/gui/text/text.pri19
-rw-r--r--src/gui/util/qgridlayoutengine_p.h8
-rw-r--r--src/gui/util/qshaderformat.cpp24
-rw-r--r--src/gui/util/qshaderformat_p.h26
-rw-r--r--src/gui/util/qshadergraph.cpp12
-rw-r--r--src/gui/util/qshadergraph_p.h16
-rw-r--r--src/gui/util/qshadergraphloader.cpp14
-rw-r--r--src/gui/util/qshadergraphloader_p.h14
-rw-r--r--src/gui/util/qshadernode.cpp16
-rw-r--r--src/gui/util/qshadernode_p.h18
-rw-r--r--src/gui/util/qshadernodeport.cpp4
-rw-r--r--src/gui/util/qshadernodeport_p.h6
-rw-r--r--src/gui/util/qshadernodesloader.cpp10
-rw-r--r--src/gui/util/qshadernodesloader_p.h10
-rw-r--r--src/gui/util/qvalidator.h1
-rw-r--r--src/gui/util/util.pri9
-rw-r--r--src/gui/vulkan/qplatformvulkaninstance.cpp5
-rw-r--r--src/gui/vulkan/qplatformvulkaninstance.h1
-rw-r--r--src/gui/vulkan/qvulkaninstance.cpp46
-rw-r--r--src/gui/vulkan/qvulkaninstance.h17
-rw-r--r--src/gui/vulkan/qvulkanwindow_p.h2
243 files changed, 38299 insertions, 1602 deletions
diff --git a/src/gui/accessible/qaccessible.cpp b/src/gui/accessible/qaccessible.cpp
index 6b825fa001..68e36de24b 100644
--- a/src/gui/accessible/qaccessible.cpp
+++ b/src/gui/accessible/qaccessible.cpp
@@ -1858,7 +1858,7 @@ Q_GUI_EXPORT QDebug operator<<(QDebug d, const QAccessibleInterface *iface)
return d;
}
d.nospace();
- d << "QAccessibleInterface(" << hex << (const void *) iface << dec;
+ d << "QAccessibleInterface(" << Qt::hex << (const void *) iface << Qt::dec;
if (iface->isValid()) {
d << " name=" << iface->text(QAccessible::Name) << ' ';
d << "role=" << qAccessibleRoleString(iface->role()) << ' ';
@@ -1897,7 +1897,7 @@ QDebug operator<<(QDebug d, const QAccessibleEvent &ev)
QDebugStateSaver saver(d);
d.nospace() << "QAccessibleEvent(";
if (ev.object()) {
- d.nospace() << "object=" << hex << ev.object() << dec;
+ d.nospace() << "object=" << Qt::hex << ev.object() << Qt::dec;
d.nospace() << "child=" << ev.child();
} else {
d.nospace() << "no object, uniqueId=" << ev.uniqueId();
diff --git a/src/gui/accessible/qaccessiblecache_p.h b/src/gui/accessible/qaccessiblecache_p.h
index a976277c1d..cf1ed04f35 100644
--- a/src/gui/accessible/qaccessiblecache_p.h
+++ b/src/gui/accessible/qaccessiblecache_p.h
@@ -73,7 +73,7 @@ public:
QAccessibleInterface *interfaceForId(QAccessible::Id id) const;
QAccessible::Id idForInterface(QAccessibleInterface *iface) const;
QAccessible::Id insert(QObject *object, QAccessibleInterface *iface) const;
- void deleteInterface(QAccessible::Id id, QObject *obj = 0);
+ void deleteInterface(QAccessible::Id id, QObject *obj = nullptr);
#ifdef Q_OS_MAC
QT_MANGLE_NAMESPACE(QMacAccessibilityElement) *elementForId(QAccessible::Id axid) const;
diff --git a/src/gui/configure.json b/src/gui/configure.json
index 6280b4dfa2..c7bafb8925 100644
--- a/src/gui/configure.json
+++ b/src/gui/configure.json
@@ -28,9 +28,9 @@
"lgmon": "boolean",
"libinput": "boolean",
"libjpeg": { "type": "enum", "values": [ "no", "qt", "system" ] },
+ "libmd4c": { "type": "enum", "values": [ "no", "qt", "system" ] },
"libpng": { "type": "enum", "values": [ "no", "qt", "system" ] },
"linuxfb": "boolean",
- "mirclient": "boolean",
"mtdev": "boolean",
"opengl": { "type": "optionalString", "values": [ "no", "yes", "desktop", "es2", "dynamic" ] },
"opengl-es-2": { "type": "void", "name": "opengl", "value": "es2" },
@@ -377,6 +377,17 @@
"-ljpeg"
]
},
+ "libmd4c": {
+ "label": "libmd4c",
+ "test": {
+ "main": "md_parse(\"hello\", 5, nullptr, nullptr);"
+ },
+ "headers": "md4c.h",
+ "sources": [
+ { "type": "pkgConfig", "args": "md4c" },
+ { "libs": "-lmd4c" }
+ ]
+ },
"libpng": {
"label": "libpng",
"test": {
@@ -395,20 +406,6 @@
{ "lib": "zlib", "condition": "features.system-zlib" }
]
},
- "mirclient": {
- "label": "Mir client libraries",
- "test": {
- "tail": "static void surfaceCreateCallback(MirSurface*, void*) {}",
- "main": [
- "u_application_lifecycle_delegate_new();",
- "mir_surface_create(0, surfaceCreateCallback, 0);"
- ]
- },
- "headers": [ "mir_toolkit/mir_client_library.h", "ubuntu/application/lifecycle_delegate.h", "EGL/egl.h" ],
- "sources": [
- { "type": "pkgConfig", "args": "egl mirclient ubuntu-platform-api libcontent-hub >= 0.2.0" }
- ]
- },
"mtdev": {
"label": "mtdev",
"test": {
@@ -491,7 +488,7 @@
"tslib": {
"label": "tslib",
"test": {
- "main": "ts_open(\"foo\", 0);"
+ "main": "ts_setup(nullptr, 0);"
},
"headers": "tslib.h",
"sources": [
@@ -1297,13 +1294,6 @@
],
"output": [ "privateFeature" ]
},
- "mirclient": {
- "label": "Mir client",
- "section": "Platform plugins",
- "autoDetect": false,
- "condition": "libs.mirclient && features.xkbcommon",
- "output": [ "privateFeature" ]
- },
"mtdev": {
"label": "mtdev",
"condition": "libs.mtdev",
@@ -1605,6 +1595,28 @@
"section": "Kernel",
"output": [ "publicFeature", "feature" ]
},
+ "textmarkdownreader": {
+ "label": "MarkdownReader",
+ "disable": "input.libmd4c == 'no'",
+ "enable": "input.libmd4c == 'system' || input.libmd4c == 'qt' || input.libmd4c == 'yes'",
+ "purpose": "Provides a Markdown (CommonMark and GitHub) reader",
+ "section": "Kernel",
+ "output": [ "publicFeature" ]
+ },
+ "system-textmarkdownreader": {
+ "label": " Using system libmd4c",
+ "disable": "input.libmd4c == 'qt'",
+ "enable": "input.libmd4c == 'system'",
+ "section": "Kernel",
+ "condition": "libs.libmd4c",
+ "output": [ "publicFeature" ]
+ },
+ "textmarkdownwriter": {
+ "label": "MarkdownWriter",
+ "purpose": "Provides a Markdown (CommonMark) writer",
+ "section": "Kernel",
+ "output": [ "publicFeature" ]
+ },
"textodfwriter": {
"label": "OdfWriter",
"purpose": "Provides an ODF writer.",
@@ -1647,7 +1659,7 @@
"label": "QClipboard",
"purpose": "Provides cut and paste operations.",
"section": "Kernel",
- "condition": "!config.integrity && !config.qnx",
+ "condition": "!config.integrity && !config.qnx && !config.rtems",
"output": [ "publicFeature", "feature" ]
},
"wheelevent": {
@@ -1791,7 +1803,7 @@
"label": "Multi process",
"purpose": "Provides support for detecting the desktop environment, launching external processes and opening URLs.",
"section": "Utilities",
- "condition": "!config.integrity",
+ "condition": "!config.integrity && !config.rtems",
"output": [ "privateFeature" ]
},
"whatsthis": {
@@ -1799,6 +1811,12 @@
"purpose": "Supports displaying \"What's this\" help.",
"section": "Widget Support",
"output": [ "publicFeature", "feature" ]
+ },
+ "raster-64bit": {
+ "label": "QPainter - 64 bit raster",
+ "purpose": "Internal painting support for 64 bit (16 bpc) rasterization.",
+ "section": "Painting",
+ "output": [ "privateFeature" ]
}
},
@@ -1830,7 +1848,7 @@ or may depend on your system and XQuartz setup."
},
{
"type": "warning",
- "condition": "features.gui && config.linux && !config.android && !features.xcb && !features.eglfs && !features.directfb && !features.linuxfb && !features.mirclient",
+ "condition": "features.gui && config.linux && !config.android && !features.xcb && !features.eglfs && !features.directfb && !features.linuxfb",
"message": "No QPA platform plugin enabled! This will
produce a Qt that cannot run GUI applications.
See \"Platform backends\" in the output of --help."
@@ -1877,6 +1895,12 @@ QMAKE_LIBDIR_OPENGL[_ES2] and QMAKE_LIBS_OPENGL[_ES2] in the mkspec for your pla
"gif", "ico", "jpeg", "system-jpeg", "png", "system-png"
]
},
+ {
+ "section": "Text formats",
+ "entries": [
+ "texthtmlparser", "cssparser", "textodfwriter", "textmarkdownreader", "system-textmarkdownreader", "textmarkdownwriter"
+ ]
+ },
"egl",
"openvg",
{
@@ -1938,7 +1962,7 @@ QMAKE_LIBDIR_OPENGL[_ES2] and QMAKE_LIBS_OPENGL[_ES2] in the mkspec for your pla
"eglfs_openwfd", "eglfs_viv", "eglfs_viv_wl", "eglfs_rcar", "eglfs_egldevice", "eglfs_gbm", "eglfs_vsp2", "eglfs_mali", "eglfs_brcm", "eglfs_x11"
]
},
- "linuxfb", "vnc", "mirclient",
+ "linuxfb", "vnc",
{
"type": "feature",
"condition": "config.integrity",
diff --git a/src/gui/doc/snippets/textdocumentendsnippet.cpp b/src/gui/doc/snippets/textdocumentendsnippet.cpp
index c8de501838..cb7abd5ca7 100644
--- a/src/gui/doc/snippets/textdocumentendsnippet.cpp
+++ b/src/gui/doc/snippets/textdocumentendsnippet.cpp
@@ -59,7 +59,7 @@ int main(int argv, char **args)
//! [0]
for (QTextBlock it = doc->begin(); it != doc->end(); it = it.next())
- cout << it.text().toStdString() << endl;
+ cout << it.text().toStdString() << Qt::endl;
//! [0]
return 0;
diff --git a/src/gui/gui.pro b/src/gui/gui.pro
index edf8124081..45c8c05162 100644
--- a/src/gui/gui.pro
+++ b/src/gui/gui.pro
@@ -49,6 +49,7 @@ qtConfig(animation): include(animation/animation.pri)
include(itemmodels/itemmodels.pri)
include(vulkan/vulkan.pri)
include(platform/platform.pri)
+include(rhi/rhi.pri)
QMAKE_LIBS += $$QMAKE_LIBS_GUI
diff --git a/src/gui/image/qbitmap.h b/src/gui/image/qbitmap.h
index f7895bb1e0..3cc360f670 100644
--- a/src/gui/image/qbitmap.h
+++ b/src/gui/image/qbitmap.h
@@ -61,7 +61,7 @@ public:
QBitmap(const QBitmap &other) : QPixmap(other) {}
// QBitmap(QBitmap &&other) : QPixmap(std::move(other)) {} // QPixmap doesn't, yet, have a move ctor
QBitmap &operator=(const QBitmap &other) { QPixmap::operator=(other); return *this; }
- QBitmap &operator=(QBitmap &&other) Q_DECL_NOTHROW { QPixmap::operator=(std::move(other)); return *this; }
+ QBitmap &operator=(QBitmap &&other) noexcept { QPixmap::operator=(std::move(other)); return *this; }
~QBitmap();
#endif
diff --git a/src/gui/image/qicon.cpp b/src/gui/image/qicon.cpp
index 80fa65daac..1d1b6d3c49 100644
--- a/src/gui/image/qicon.cpp
+++ b/src/gui/image/qicon.cpp
@@ -663,7 +663,7 @@ QFactoryLoader *qt_iconEngineFactoryLoader()
/*!
Constructs a null icon.
*/
-QIcon::QIcon() Q_DECL_NOEXCEPT
+QIcon::QIcon() noexcept
: d(0)
{
}
@@ -1496,7 +1496,7 @@ QDebug operator<<(QDebug dbg, const QIcon &i)
if (!i.name().isEmpty())
dbg << i.name() << ',';
dbg << "availableSizes[normal,Off]=" << i.availableSizes()
- << ",cacheKey=" << showbase << hex << i.cacheKey() << dec << noshowbase;
+ << ",cacheKey=" << Qt::showbase << Qt::hex << i.cacheKey() << Qt::dec << Qt::noshowbase;
}
dbg << ')';
return dbg;
diff --git a/src/gui/image/qicon.h b/src/gui/image/qicon.h
index 8870950982..735a3e134d 100644
--- a/src/gui/image/qicon.h
+++ b/src/gui/image/qicon.h
@@ -58,23 +58,19 @@ public:
enum Mode { Normal, Disabled, Active, Selected };
enum State { On, Off };
- QIcon() Q_DECL_NOEXCEPT;
+ QIcon() noexcept;
QIcon(const QPixmap &pixmap);
QIcon(const QIcon &other);
-#ifdef Q_COMPILER_RVALUE_REFS
- QIcon(QIcon &&other) Q_DECL_NOEXCEPT
+ QIcon(QIcon &&other) noexcept
: d(other.d)
{ other.d = nullptr; }
-#endif
explicit QIcon(const QString &fileName); // file or resource name
explicit QIcon(QIconEngine *engine);
~QIcon();
QIcon &operator=(const QIcon &other);
-#ifdef Q_COMPILER_RVALUE_REFS
- inline QIcon &operator=(QIcon &&other) Q_DECL_NOEXCEPT
+ inline QIcon &operator=(QIcon &&other) noexcept
{ swap(other); return *this; }
-#endif
- inline void swap(QIcon &other) Q_DECL_NOEXCEPT
+ inline void swap(QIcon &other) noexcept
{ qSwap(d, other.d); }
operator QVariant() const;
diff --git a/src/gui/image/qiconengine.h b/src/gui/image/qiconengine.h
index 0c5b51dc71..89fed5ccda 100644
--- a/src/gui/image/qiconengine.h
+++ b/src/gui/image/qiconengine.h
@@ -94,7 +94,7 @@ public:
virtual void virtual_hook(int id, void *data);
private:
- QIconEngine &operator=(const QIconEngine &other) Q_DECL_EQ_DELETE;
+ QIconEngine &operator=(const QIconEngine &other) = delete;
};
#if QT_DEPRECATED_SINCE(5, 0)
diff --git a/src/gui/image/qimage.cpp b/src/gui/image/qimage.cpp
index ebd9037e44..917dde3b0f 100644
--- a/src/gui/image/qimage.cpp
+++ b/src/gui/image/qimage.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtGui module of the Qt Toolkit.
@@ -38,8 +38,10 @@
****************************************************************************/
#include "qimage.h"
-#include "qdatastream.h"
+
#include "qbuffer.h"
+#include "qdatastream.h"
+#include "qcolortransform.h"
#include "qmap.h"
#include "qmatrix.h"
#include "qtransform.h"
@@ -54,6 +56,7 @@
#include <stdlib.h>
#include <limits.h>
#include <qpa/qplatformpixmap.h>
+#include <private/qcolortransform_p.h>
#include <private/qdrawhelper_p.h>
#include <private/qmemrotate_p.h>
#include <private/qimagescale_p.h>
@@ -738,7 +741,7 @@ bool QImageData::checkForAlphaPixels() const
\sa isNull()
*/
-QImage::QImage() Q_DECL_NOEXCEPT
+QImage::QImage() noexcept
: QPaintDevice()
{
d = 0;
@@ -1103,6 +1106,7 @@ static void copyMetadata(QImageData *dst, const QImageData *src)
// Doesn't copy colortable and alpha_clut, or offset.
copyPhysicalMetadata(dst, src);
dst->text = src->text;
+ dst->colorSpace = src->colorSpace;
}
static void copyMetadata(QImage *dst, const QImage &src)
@@ -1392,7 +1396,7 @@ void QImage::setColorTable(const QVector<QRgb> colors)
#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
d->colortable = colors;
#else
- d->colortable = qMove(const_cast<QVector<QRgb>&>(colors));
+ d->colortable = std::move(const_cast<QVector<QRgb>&>(colors));
#endif
d->has_alpha_clut = false;
for (int i = 0; i < d->colortable.size(); ++i) {
@@ -4686,12 +4690,14 @@ QImage QImage::smoothScaled(int w, int h) const {
case QImage::Format_RGBX8888:
#endif
case QImage::Format_RGBA8888_Premultiplied:
+#if QT_CONFIG(raster_64bit)
case QImage::Format_RGBX64:
case QImage::Format_RGBA64_Premultiplied:
break;
case QImage::Format_RGBA64:
src = src.convertToFormat(QImage::Format_RGBA64_Premultiplied);
break;
+#endif
default:
if (src.hasAlphaChannel())
src = src.convertToFormat(QImage::Format_ARGB32_Premultiplied);
@@ -4949,6 +4955,132 @@ QTransform QImage::trueMatrix(const QTransform &matrix, int w, int h)
return matrix * QTransform().translate(-delta.x(), -delta.y());
}
+/*!
+ \since 5.14
+
+ Sets the image color space to \a colorSpace without performing any conversions on image data.
+
+ \sa colorSpace()
+*/
+void QImage::setColorSpace(const QColorSpace &colorSpace)
+{
+ if (!d)
+ return;
+ if (d->colorSpace == colorSpace)
+ return;
+ if (!isDetached()) // Detach only if shared, not for read-only data.
+ detach();
+ d->colorSpace = colorSpace;
+}
+
+/*!
+ \since 5.14
+
+ Converts the image to \a colorSpace.
+
+ If the image has no valid color space, the method does nothing.
+
+ \sa convertedToColorSpace(), setColorSpace()
+*/
+void QImage::convertToColorSpace(const QColorSpace &colorSpace)
+{
+ if (!d)
+ return;
+ if (!d->colorSpace.isValid())
+ return;
+ if (!colorSpace.isValid()) {
+ qWarning() << "QImage::convertToColorSpace: Output colorspace is not valid";
+ return;
+ }
+ detach();
+ applyColorTransform(d->colorSpace.transformationToColorSpace(colorSpace));
+ d->colorSpace = colorSpace;
+}
+
+/*!
+ \since 5.14
+
+ Returns the image converted to \a colorSpace.
+
+ If the image has no valid color space, a null QImage is returned.
+
+ \sa convertToColorSpace()
+*/
+QImage QImage::convertedToColorSpace(const QColorSpace &colorSpace) const
+{
+ if (!d || !d->colorSpace.isValid() || !colorSpace.isValid())
+ return QImage();
+ QImage image = copy();
+ image.convertToColorSpace(colorSpace);
+ return image;
+}
+
+/*!
+ \since 5.14
+
+ Returns the color space of the image if a color space is defined.
+*/
+QColorSpace QImage::colorSpace() const
+{
+ if (!d)
+ return QColorSpace::Undefined;
+ return d->colorSpace;
+}
+
+/*!
+ \since 5.14
+
+ Applies the color transformation \a transform to all pixels in the image.
+*/
+void QImage::applyColorTransform(const QColorTransform &transform)
+{
+ QImage::Format oldFormat = format();
+ if (depth() > 32) {
+ if (format() != QImage::Format_RGBX64 && format() != QImage::Format_RGBA64
+ && format() != QImage::Format_RGBA64_Premultiplied)
+ *this = std::move(*this).convertToFormat(QImage::Format_RGBA64);
+ } else if (format() != QImage::Format_ARGB32 && format() != QImage::Format_RGB32
+ && format() != QImage::Format_ARGB32_Premultiplied) {
+ if (hasAlphaChannel())
+ *this = std::move(*this).convertToFormat(QImage::Format_ARGB32);
+ else
+ *this = std::move(*this).convertToFormat(QImage::Format_RGB32);
+ }
+
+ QColorTransformPrivate::TransformFlags flags = QColorTransformPrivate::Unpremultiplied;
+ switch (format()) {
+ case Format_ARGB32_Premultiplied:
+ case Format_RGBA64_Premultiplied:
+ flags = QColorTransformPrivate::Premultiplied;
+ break;
+ case Format_RGB32:
+ case Format_RGBX64:
+ flags = QColorTransformPrivate::InputOpaque;
+ break;
+ case Format_ARGB32:
+ case Format_RGBA64:
+ break;
+ default:
+ Q_UNREACHABLE();
+ }
+
+ if (depth() > 32) {
+ for (int i = 0; i < height(); ++i) {
+ QRgba64 *scanline = reinterpret_cast<QRgba64 *>(scanLine(i));
+ transform.d_func()->apply(scanline, scanline, width(), flags);
+ }
+ } else {
+ for (int i = 0; i < height(); ++i) {
+ QRgb *scanline = reinterpret_cast<QRgb *>(scanLine(i));
+ transform.d_func()->apply(scanline, scanline, width(), flags);
+ }
+ }
+
+ if (oldFormat != format())
+ *this = std::move(*this).convertToFormat(oldFormat);
+}
+
+
bool QImageData::convertInPlace(QImage::Format newFormat, Qt::ImageConversionFlags flags)
{
if (format == newFormat)
@@ -5424,7 +5556,7 @@ Q_STATIC_ASSERT(sizeof(pixelformats) / sizeof(*pixelformats) == QImage::NImageFo
/*!
Returns the QImage::Format as a QPixelFormat
*/
-QPixelFormat QImage::pixelFormat() const Q_DECL_NOTHROW
+QPixelFormat QImage::pixelFormat() const noexcept
{
return toPixelFormat(format());
}
@@ -5432,7 +5564,7 @@ QPixelFormat QImage::pixelFormat() const Q_DECL_NOTHROW
/*!
Converts \a format into a QPixelFormat
*/
-QPixelFormat QImage::toPixelFormat(QImage::Format format) Q_DECL_NOTHROW
+QPixelFormat QImage::toPixelFormat(QImage::Format format) noexcept
{
Q_ASSERT(static_cast<int>(format) < NImageFormats);
return pixelformats[format];
@@ -5441,7 +5573,7 @@ QPixelFormat QImage::toPixelFormat(QImage::Format format) Q_DECL_NOTHROW
/*!
Converts \a format into a QImage::Format
*/
-QImage::Format QImage::toImageFormat(QPixelFormat format) Q_DECL_NOTHROW
+QImage::Format QImage::toImageFormat(QPixelFormat format) noexcept
{
for (int i = 0; i < NImageFormats; i++) {
if (format == pixelformats[i])
@@ -5457,7 +5589,7 @@ Q_GUI_EXPORT void qt_imageTransform(QImage &src, QImageIOHandler::Transformation
if (orient == QImageIOHandler::TransformationRotate270) {
src = rotated270(src);
} else {
- src = qMove(src).mirrored(orient & QImageIOHandler::TransformationMirror,
+ src = std::move(src).mirrored(orient & QImageIOHandler::TransformationMirror,
orient & QImageIOHandler::TransformationFlip);
if (orient & QImageIOHandler::TransformationRotate90)
src = rotated90(src);
diff --git a/src/gui/image/qimage.h b/src/gui/image/qimage.h
index 8335e117f2..7c68168be8 100644
--- a/src/gui/image/qimage.h
+++ b/src/gui/image/qimage.h
@@ -61,9 +61,11 @@ Q_FORWARD_DECLARE_MUTABLE_CG_TYPE(CGImage);
QT_BEGIN_NAMESPACE
+class QColorSpace;
+class QColorTransform;
class QIODevice;
-class QStringList;
class QMatrix;
+class QStringList;
class QTransform;
class QVariant;
template <class T> class QList;
@@ -135,7 +137,7 @@ public:
};
Q_ENUM(Format)
- QImage() Q_DECL_NOEXCEPT;
+ QImage() noexcept;
QImage(const QSize &size, Format format);
QImage(int width, int height, Format format);
QImage(uchar *data, int width, int height, Format format, QImageCleanupFunction cleanupFunction = nullptr, void *cleanupInfo = nullptr);
@@ -149,19 +151,15 @@ public:
explicit QImage(const QString &fileName, const char *format = nullptr);
QImage(const QImage &);
-#ifdef Q_COMPILER_RVALUE_REFS
- inline QImage(QImage &&other) Q_DECL_NOEXCEPT
+ inline QImage(QImage &&other) noexcept
: QPaintDevice(), d(nullptr)
{ qSwap(d, other.d); }
-#endif
~QImage();
QImage &operator=(const QImage &);
-#ifdef Q_COMPILER_RVALUE_REFS
- inline QImage &operator=(QImage &&other) Q_DECL_NOEXCEPT
+ inline QImage &operator=(QImage &&other) noexcept
{ qSwap(d, other.d); return *this; }
-#endif
- inline void swap(QImage &other) Q_DECL_NOEXCEPT
+ inline void swap(QImage &other) noexcept
{ qSwap(d, other.d); }
bool isNull() const;
@@ -289,17 +287,23 @@ public:
QImage mirrored(bool horizontally = false, bool vertically = true) const &
{ return mirrored_helper(horizontally, vertically); }
QImage &&mirrored(bool horizontally = false, bool vertically = true) &&
- { mirrored_inplace(horizontally, vertically); return qMove(*this); }
+ { mirrored_inplace(horizontally, vertically); return std::move(*this); }
QImage rgbSwapped() const &
{ return rgbSwapped_helper(); }
QImage &&rgbSwapped() &&
- { rgbSwapped_inplace(); return qMove(*this); }
+ { rgbSwapped_inplace(); return std::move(*this); }
#else
QImage mirrored(bool horizontally = false, bool vertically = true) const;
QImage rgbSwapped() const;
#endif
void invertPixels(InvertMode = InvertRgb);
+ QColorSpace colorSpace() const;
+ QImage convertedToColorSpace(const QColorSpace &) const;
+ void convertToColorSpace(const QColorSpace &);
+ void setColorSpace(const QColorSpace &);
+
+ void applyColorTransform(const QColorTransform &transform);
bool load(QIODevice *device, const char* format);
bool load(const QString &fileName, const char *format = nullptr);
@@ -333,9 +337,9 @@ public:
QString text(const QString &key = QString()) const;
void setText(const QString &key, const QString &value);
- QPixelFormat pixelFormat() const Q_DECL_NOTHROW;
- static QPixelFormat toPixelFormat(QImage::Format format) Q_DECL_NOTHROW;
- static QImage::Format toImageFormat(QPixelFormat format) Q_DECL_NOTHROW;
+ QPixelFormat pixelFormat() const noexcept;
+ static QPixelFormat toPixelFormat(QImage::Format format) noexcept;
+ static QImage::Format toImageFormat(QPixelFormat format) noexcept;
// Platform specific conversion functions
#if defined(Q_OS_DARWIN) || defined(Q_QDOC)
diff --git a/src/gui/image/qimage_conversions.cpp b/src/gui/image/qimage_conversions.cpp
index 82ffb8af8b..837ac88470 100644
--- a/src/gui/image/qimage_conversions.cpp
+++ b/src/gui/image/qimage_conversions.cpp
@@ -39,7 +39,7 @@
#include <private/qdrawhelper_p.h>
#include <private/qguiapplication_p.h>
-#include <private/qcolorprofile_p.h>
+#include <private/qcolortrclut_p.h>
#include <private/qendian_p.h>
#include <private/qsimd_p.h>
#include <private/qimage_p.h>
@@ -100,7 +100,7 @@ const uchar *qt_get_bitflip_array()
void qGamma_correct_back_to_linear_cs(QImage *image)
{
- const QColorProfile *cp = QGuiApplicationPrivate::instance()->colorProfileForA32Text();
+ const QColorTrcLut *cp = QGuiApplicationPrivate::instance()->colorProfileForA32Text();
if (!cp)
return;
// gamma correct the pixels back to linear color space...
diff --git a/src/gui/image/qimage_p.h b/src/gui/image/qimage_p.h
index d88ad2d1d2..9da6acd0a7 100644
--- a/src/gui/image/qimage_p.h
+++ b/src/gui/image/qimage_p.h
@@ -51,7 +51,9 @@
// We mean it.
//
+#include <QtGui/qcolorspace.h>
#include <QtGui/private/qtguiglobal_p.h>
+#include <QtGui/qimage.h>
#include <QtCore/private/qnumeric_p.h>
#include <QMap>
@@ -65,7 +67,7 @@ struct Q_GUI_EXPORT QImageData { // internal image data
QImageData();
~QImageData();
static QImageData *create(const QSize &size, QImage::Format format);
- static QImageData *create(uchar *data, int w, int h, int bpl, QImage::Format format, bool readOnly, QImageCleanupFunction cleanupFunction = 0, void *cleanupInfo = 0);
+ static QImageData *create(uchar *data, int w, int h, int bpl, QImage::Format format, bool readOnly, QImageCleanupFunction cleanupFunction = nullptr, void *cleanupInfo = nullptr);
QAtomicInt ref;
@@ -106,6 +108,8 @@ struct Q_GUI_EXPORT QImageData { // internal image data
QPaintEngine *paintEngine;
+ QColorSpace colorSpace;
+
struct ImageSizeParameters {
qsizetype bytesPerLine;
qsizetype totalSize;
diff --git a/src/gui/image/qimagereader.cpp b/src/gui/image/qimagereader.cpp
index 61f20e0c65..ae433ff651 100644
--- a/src/gui/image/qimagereader.cpp
+++ b/src/gui/image/qimagereader.cpp
@@ -1115,8 +1115,10 @@ bool QImageReader::autoTransform() const
case QImageReaderPrivate::DoNotApplyTransform:
return false;
case QImageReaderPrivate::UsePluginDefault:
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
if (d->initHandler())
return d->handler->supportsOption(QImageIOHandler::TransformedByDefault);
+#endif
Q_FALLTHROUGH();
default:
break;
diff --git a/src/gui/image/qmovie.cpp b/src/gui/image/qmovie.cpp
index 79203c7b98..c9344fe2a1 100644
--- a/src/gui/image/qmovie.cpp
+++ b/src/gui/image/qmovie.cpp
@@ -175,6 +175,7 @@
#include "qmovie.h"
#include "qglobal.h"
+#include "qelapsedtimer.h"
#include "qimage.h"
#include "qimagereader.h"
#include "qpixmap.h"
@@ -437,7 +438,7 @@ QFrameInfo QMoviePrivate::infoForFrame(int frameNumber)
*/
bool QMoviePrivate::next()
{
- QTime time;
+ QElapsedTimer time;
time.start();
QFrameInfo info = infoForFrame(nextFrameNumber);
if (!info.isValid())
diff --git a/src/gui/image/qpicture.cpp b/src/gui/image/qpicture.cpp
index bba36b09cd..fe1e4d2c9b 100644
--- a/src/gui/image/qpicture.cpp
+++ b/src/gui/image/qpicture.cpp
@@ -54,6 +54,7 @@
#include "qpainter.h"
#include "qpainterpath.h"
#include "qpixmap.h"
+#include "qregexp.h"
#include "qregion.h"
#include "qdebug.h"
diff --git a/src/gui/image/qpicture.h b/src/gui/image/qpicture.h
index ec7b4bd7e3..189e57b9a3 100644
--- a/src/gui/image/qpicture.h
+++ b/src/gui/image/qpicture.h
@@ -78,11 +78,9 @@ public:
void setBoundingRect(const QRect &r);
QPicture& operator=(const QPicture &p);
-#ifdef Q_COMPILER_RVALUE_REFS
- inline QPicture &operator=(QPicture &&other) Q_DECL_NOEXCEPT
+ inline QPicture &operator=(QPicture &&other) noexcept
{ qSwap(d_ptr, other.d_ptr); return *this; }
-#endif
- inline void swap(QPicture &other) Q_DECL_NOEXCEPT
+ inline void swap(QPicture &other) noexcept
{ d_ptr.swap(other.d_ptr); }
void detach();
bool isDetached() const;
diff --git a/src/gui/image/qpixmap.cpp b/src/gui/image/qpixmap.cpp
index 5b4d218603..399ad7453d 100644
--- a/src/gui/image/qpixmap.cpp
+++ b/src/gui/image/qpixmap.cpp
@@ -1689,7 +1689,7 @@ QDebug operator<<(QDebug dbg, const QPixmap &r)
} else {
dbg << r.size() << ",depth=" << r.depth()
<< ",devicePixelRatio=" << r.devicePixelRatio()
- << ",cacheKey=" << showbase << hex << r.cacheKey() << dec << noshowbase;
+ << ",cacheKey=" << Qt::showbase << Qt::hex << r.cacheKey() << Qt::dec << Qt::noshowbase;
}
dbg << ')';
return dbg;
diff --git a/src/gui/image/qpixmap.h b/src/gui/image/qpixmap.h
index 13c81f18d0..8c1395857e 100644
--- a/src/gui/image/qpixmap.h
+++ b/src/gui/image/qpixmap.h
@@ -73,11 +73,9 @@ public:
~QPixmap();
QPixmap &operator=(const QPixmap &);
-#ifdef Q_COMPILER_RVALUE_REFS
- inline QPixmap &operator=(QPixmap &&other) Q_DECL_NOEXCEPT
+ inline QPixmap &operator=(QPixmap &&other) noexcept
{ qSwap(data, other.data); return *this; }
-#endif
- inline void swap(QPixmap &other) Q_DECL_NOEXCEPT
+ inline void swap(QPixmap &other) noexcept
{ qSwap(data, other.data); }
operator QVariant() const;
@@ -139,12 +137,10 @@ public:
QImage toImage() const;
static QPixmap fromImage(const QImage &image, Qt::ImageConversionFlags flags = Qt::AutoColor);
static QPixmap fromImageReader(QImageReader *imageReader, Qt::ImageConversionFlags flags = Qt::AutoColor);
-#ifdef Q_COMPILER_RVALUE_REFS
static QPixmap fromImage(QImage &&image, Qt::ImageConversionFlags flags = Qt::AutoColor)
{
return fromImageInPlace(image, flags);
}
-#endif
bool load(const QString& fileName, const char *format = nullptr, Qt::ImageConversionFlags flags = Qt::AutoColor);
bool loadFromData(const uchar *buf, uint len, const char* format = nullptr, Qt::ImageConversionFlags flags = Qt::AutoColor);
diff --git a/src/gui/image/qpixmapcache.cpp b/src/gui/image/qpixmapcache.cpp
index 66907bebd7..a41ec8f35c 100644
--- a/src/gui/image/qpixmapcache.cpp
+++ b/src/gui/image/qpixmapcache.cpp
@@ -176,7 +176,7 @@ bool QPixmapCache::Key::operator ==(const Key &key) const
Otherwise, if pixmap was flushed, the key is no longer valid.
\since 5.7
*/
-bool QPixmapCache::Key::isValid() const Q_DECL_NOTHROW
+bool QPixmapCache::Key::isValid() const noexcept
{
return d && d->isValid;
}
diff --git a/src/gui/image/qpixmapcache.h b/src/gui/image/qpixmapcache.h
index 12d05b00f3..55af35a5d9 100644
--- a/src/gui/image/qpixmapcache.h
+++ b/src/gui/image/qpixmapcache.h
@@ -55,18 +55,16 @@ public:
public:
Key();
Key(const Key &other);
-#ifdef Q_COMPILER_RVALUE_REFS
- Key(Key &&other) Q_DECL_NOTHROW : d(other.d) { other.d = nullptr; }
- Key &operator =(Key &&other) Q_DECL_NOTHROW { swap(other); return *this; }
-#endif
+ Key(Key &&other) noexcept : d(other.d) { other.d = nullptr; }
+ Key &operator =(Key &&other) noexcept { swap(other); return *this; }
~Key();
bool operator ==(const Key &key) const;
inline bool operator !=(const Key &key) const
{ return !operator==(key); }
Key &operator =(const Key &other);
- void swap(Key &other) Q_DECL_NOTHROW { qSwap(d, other.d); }
- bool isValid() const Q_DECL_NOTHROW;
+ void swap(Key &other) noexcept { qSwap(d, other.d); }
+ bool isValid() const noexcept;
private:
KeyData *d;
diff --git a/src/gui/image/qpixmapcache_p.h b/src/gui/image/qpixmapcache_p.h
index 3c57367514..ab8e2b7558 100644
--- a/src/gui/image/qpixmapcache_p.h
+++ b/src/gui/image/qpixmapcache_p.h
@@ -87,7 +87,7 @@ public:
&& !d->image.d->paintEngine->isActive())
{
delete d->image.d->paintEngine;
- d->image.d->paintEngine = 0;
+ d->image.d->paintEngine = nullptr;
}
}
}
diff --git a/src/gui/image/qpnghandler.cpp b/src/gui/image/qpnghandler.cpp
index 1c269213e2..39d0807606 100644
--- a/src/gui/image/qpnghandler.cpp
+++ b/src/gui/image/qpnghandler.cpp
@@ -42,6 +42,7 @@
#ifndef QT_NO_IMAGEFORMAT_PNG
#include <qcoreapplication.h>
+#include <qdebug.h>
#include <qiodevice.h>
#include <qimage.h>
#include <qlist.h>
@@ -50,6 +51,10 @@
#include <private/qimage_p.h> // for qt_getImageText
+#include <qcolorspace.h>
+#include <private/qcolorspace_p.h>
+#include <private/qicc_p.h>
+
#include <png.h>
#include <pngconf.h>
@@ -96,9 +101,16 @@ public:
ReadingEnd,
Error
};
+ // Defines the order of how the various ways of setting colorspace overrides eachother:
+ enum ColorSpaceState {
+ Undefined = 0,
+ GammaChrm = 1, // gAMA+cHRM chunks
+ Srgb = 2, // sRGB chunk
+ Icc = 3 // iCCP chunk
+ };
QPngHandlerPrivate(QPngHandler *qq)
- : gamma(0.0), fileGamma(0.0), quality(50), compression(50), png_ptr(0), info_ptr(0), end_info(0), state(Ready), q(qq)
+ : gamma(0.0), fileGamma(0.0), quality(50), compression(50), colorSpaceState(Undefined), png_ptr(0), info_ptr(0), end_info(0), state(Ready), q(qq)
{ }
float gamma;
@@ -108,6 +120,8 @@ public:
QString description;
QSize scaledSize;
QStringList readTexts;
+ QColorSpace colorSpace;
+ ColorSpaceState colorSpaceState;
png_struct *png_ptr;
png_info *info_ptr;
@@ -226,11 +240,8 @@ void qpiw_flush_fn(png_structp /* png_ptr */)
}
static
-void setup_qt(QImage& image, png_structp png_ptr, png_infop info_ptr, QSize scaledSize, bool *doScaledRead, float screen_gamma=0.0, float file_gamma=0.0)
+void setup_qt(QImage& image, png_structp png_ptr, png_infop info_ptr, QSize scaledSize, bool *doScaledRead)
{
- if (screen_gamma != 0.0 && file_gamma != 0.0)
- png_set_gamma(png_ptr, 1.0f / screen_gamma, file_gamma);
-
png_uint_32 width = 0;
png_uint_32 height = 0;
int bit_depth = 0;
@@ -585,10 +596,45 @@ bool QPngHandlerPrivate::readPngHeader()
readPngTexts(info_ptr);
+#ifdef PNG_iCCP_SUPPORTED
+ if (png_get_valid(png_ptr, info_ptr, PNG_INFO_iCCP)) {
+ png_charp name = nullptr;
+ int compressionType = 0;
+#if (PNG_LIBPNG_VER < 10500)
+ png_charp profileData = nullptr;
+#else
+ png_bytep profileData = nullptr;
+#endif
+ png_uint_32 profLen;
+ png_get_iCCP(png_ptr, info_ptr, &name, &compressionType, &profileData, &profLen);
+ if (!QIcc::fromIccProfile(QByteArray::fromRawData((const char *)profileData, profLen), &colorSpace)) {
+ qWarning() << "QPngHandler: Failed to parse ICC profile";
+ } else {
+ colorSpaceState = Icc;
+ }
+ }
+#endif
+ if (png_get_valid(png_ptr, info_ptr, PNG_INFO_sRGB)) {
+ int rendering_intent = -1;
+ png_get_sRGB(png_ptr, info_ptr, &rendering_intent);
+ // We don't actually care about the rendering_intent, just that it is valid
+ if (rendering_intent >= 0 && rendering_intent <= 3 && colorSpaceState <= Srgb) {
+ colorSpace = QColorSpace::SRgb;
+ colorSpaceState = Srgb;
+ }
+ }
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_gAMA)) {
double file_gamma = 0.0;
png_get_gAMA(png_ptr, info_ptr, &file_gamma);
fileGamma = file_gamma;
+ if (fileGamma > 0.0f && colorSpaceState <= GammaChrm) {
+ QColorSpacePrivate *csPrivate = colorSpace.d_func();
+ csPrivate->gamut = QColorSpace::Gamut::SRgb;
+ csPrivate->transferFunction = QColorSpace::TransferFunction::Gamma;
+ csPrivate->gamma = fileGamma;
+ csPrivate->initialize();
+ colorSpaceState = GammaChrm;
+ }
}
state = ReadHeader;
@@ -613,8 +659,19 @@ bool QPngHandlerPrivate::readPngImage(QImage *outImage)
return false;
}
+ if (gamma != 0.0 && fileGamma != 0.0) {
+ // This configuration forces gamma correction and
+ // thus changes the output colorspace
+ png_set_gamma(png_ptr, 1.0f / gamma, fileGamma);
+ QColorSpacePrivate *csPrivate = colorSpace.d_func();
+ csPrivate->transferFunction = QColorSpace::TransferFunction::Gamma;
+ csPrivate->gamma = gamma;
+ csPrivate->initialize();
+ colorSpaceState = GammaChrm;
+ }
+
bool doScaledRead = false;
- setup_qt(*outImage, png_ptr, info_ptr, scaledSize, &doScaledRead, gamma, fileGamma);
+ setup_qt(*outImage, png_ptr, info_ptr, scaledSize, &doScaledRead);
if (outImage->isNull()) {
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
@@ -683,6 +740,9 @@ bool QPngHandlerPrivate::readPngImage(QImage *outImage)
if (scaledSize.isValid() && outImage->size() != scaledSize)
*outImage = outImage->scaled(scaledSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+ if (colorSpaceState > Undefined && colorSpace.isValid())
+ outImage->setColorSpace(colorSpace);
+
return true;
}
diff --git a/src/gui/image/qppmhandler_p.h b/src/gui/image/qppmhandler_p.h
index f3c9d0f139..2f3811b759 100644
--- a/src/gui/image/qppmhandler_p.h
+++ b/src/gui/image/qppmhandler_p.h
@@ -71,7 +71,7 @@ public:
QByteArray name() const override;
#endif
- static bool canRead(QIODevice *device, QByteArray *subType = 0);
+ static bool canRead(QIODevice *device, QByteArray *subType = nullptr);
QVariant option(ImageOption option) const override;
void setOption(ImageOption option, const QVariant &value) override;
diff --git a/src/gui/image/qxbmhandler.cpp b/src/gui/image/qxbmhandler.cpp
index 65a5b63bc7..3cd15b3e4d 100644
--- a/src/gui/image/qxbmhandler.cpp
+++ b/src/gui/image/qxbmhandler.cpp
@@ -44,6 +44,7 @@
#include <qimage.h>
#include <qiodevice.h>
+#include <qregexp.h>
#include <qvariant.h>
#include <stdio.h>
diff --git a/src/gui/image/qxpmhandler.cpp b/src/gui/image/qxpmhandler.cpp
index a32dfda96d..deff56aa58 100644
--- a/src/gui/image/qxpmhandler.cpp
+++ b/src/gui/image/qxpmhandler.cpp
@@ -45,6 +45,7 @@
#include <qbytearraymatcher.h>
#include <qimage.h>
#include <qmap.h>
+#include <qregexp.h>
#include <qtextstream.h>
#include <qvariant.h>
@@ -1128,8 +1129,8 @@ static bool write_xpm_image(const QImage &sourceImage, QIODevice *device, const
// write header
QTextStream s(device);
- s << "/* XPM */" << endl
- << "static char *" << fbname(fileName) << "[]={" << endl
+ s << "/* XPM */" << Qt::endl
+ << "static char *" << fbname(fileName) << "[]={" << Qt::endl
<< '\"' << w << ' ' << h << ' ' << ncolors << ' ' << cpp << '\"';
// write palette
@@ -1146,7 +1147,7 @@ static bool write_xpm_image(const QImage &sourceImage, QIODevice *device, const
qGreen(color),
qBlue(color));
++c;
- s << ',' << endl << line;
+ s << ',' << Qt::endl << line;
}
// write pixels, limit to 4 characters per pixel
@@ -1168,9 +1169,9 @@ static bool write_xpm_image(const QImage &sourceImage, QIODevice *device, const
}
}
}
- s << ',' << endl << '\"' << line << '\"';
+ s << ',' << Qt::endl << '\"' << line << '\"';
}
- s << "};" << endl;
+ s << "};" << Qt::endl;
return (s.status() == QTextStream::Ok);
}
diff --git a/src/gui/itemmodels/qstandarditemmodel_p.h b/src/gui/itemmodels/qstandarditemmodel_p.h
index 23d2938bc4..97c2e6f01b 100644
--- a/src/gui/itemmodels/qstandarditemmodel_p.h
+++ b/src/gui/itemmodels/qstandarditemmodel_p.h
@@ -109,11 +109,11 @@ class QStandardItemPrivate
Q_DECLARE_PUBLIC(QStandardItem)
public:
inline QStandardItemPrivate()
- : model(0),
- parent(0),
+ : model(nullptr),
+ parent(nullptr),
rows(0),
columns(0),
- q_ptr(0),
+ q_ptr(nullptr),
lastKnownIndex(-1)
{ }
@@ -220,10 +220,10 @@ public:
if (!index.isValid())
return root.data();
if (index.model() != q)
- return 0;
+ return nullptr;
QStandardItem *parent = static_cast<QStandardItem*>(index.internalPointer());
- if (parent == 0)
- return 0;
+ if (parent == nullptr)
+ return nullptr;
return parent->child(index.row(), index.column());
}
diff --git a/src/gui/kernel/kernel.pri b/src/gui/kernel/kernel.pri
index 1f137fc46f..9c80f1e2cc 100644
--- a/src/gui/kernel/kernel.pri
+++ b/src/gui/kernel/kernel.pri
@@ -21,7 +21,7 @@ HEADERS += \
kernel/qplatforminputcontextplugin_p.h \
kernel/qplatformintegrationfactory_p.h \
kernel/qplatformintegrationplugin.h \
- kernel/qplatformtheme.h\
+ kernel/qplatformtheme.h \
kernel/qplatformtheme_p.h \
kernel/qplatformthemefactory_p.h \
kernel/qplatformthemeplugin.h \
diff --git a/src/gui/kernel/qcursor.cpp b/src/gui/kernel/qcursor.cpp
index 9e4787589f..1ba8760a9d 100644
--- a/src/gui/kernel/qcursor.cpp
+++ b/src/gui/kernel/qcursor.cpp
@@ -494,7 +494,7 @@ QCursor::QCursor(Qt::CursorShape shape)
\sa operator!=(const QCursor &lhs, const QCursor &rhs)
*/
-bool operator==(const QCursor &lhs, const QCursor &rhs) Q_DECL_NOTHROW
+bool operator==(const QCursor &lhs, const QCursor &rhs) noexcept
{
if (lhs.d == rhs.d)
return true; // Copy or same shape
diff --git a/src/gui/kernel/qcursor.h b/src/gui/kernel/qcursor.h
index d62ee7a053..7966e35840 100644
--- a/src/gui/kernel/qcursor.h
+++ b/src/gui/kernel/qcursor.h
@@ -86,13 +86,11 @@ public:
QCursor(const QCursor &cursor);
~QCursor();
QCursor &operator=(const QCursor &cursor);
-#ifdef Q_COMPILER_RVALUE_REFS
- QCursor(QCursor &&other) Q_DECL_NOTHROW : d(other.d) { other.d = nullptr; }
- inline QCursor &operator=(QCursor &&other) Q_DECL_NOTHROW
+ QCursor(QCursor &&other) noexcept : d(other.d) { other.d = nullptr; }
+ inline QCursor &operator=(QCursor &&other) noexcept
{ swap(other); return *this; }
-#endif
- void swap(QCursor &other) Q_DECL_NOTHROW { qSwap(d, other.d); }
+ void swap(QCursor &other) noexcept { qSwap(d, other.d); }
operator QVariant() const;
@@ -112,13 +110,13 @@ public:
inline static void setPos(QScreen *screen, const QPoint &p) { setPos(screen, p.x(), p.y()); }
private:
- friend Q_GUI_EXPORT bool operator==(const QCursor &lhs, const QCursor &rhs) Q_DECL_NOTHROW;
+ friend Q_GUI_EXPORT bool operator==(const QCursor &lhs, const QCursor &rhs) noexcept;
QCursorData *d;
};
Q_DECLARE_SHARED_NOT_MOVABLE_UNTIL_QT6(QCursor)
-Q_GUI_EXPORT bool operator==(const QCursor &lhs, const QCursor &rhs) Q_DECL_NOTHROW;
-inline bool operator!=(const QCursor &lhs, const QCursor &rhs) Q_DECL_NOTHROW { return !(lhs == rhs); }
+Q_GUI_EXPORT bool operator==(const QCursor &lhs, const QCursor &rhs) noexcept;
+inline bool operator!=(const QCursor &lhs, const QCursor &rhs) noexcept { return !(lhs == rhs); }
/*****************************************************************************
QCursor stream functions
diff --git a/src/gui/kernel/qdnd_p.h b/src/gui/kernel/qdnd_p.h
index 8f8eb03f87..b1219c8658 100644
--- a/src/gui/kernel/qdnd_p.h
+++ b/src/gui/kernel/qdnd_p.h
@@ -73,9 +73,9 @@ class QDragPrivate : public QObjectPrivate
{
public:
QDragPrivate()
- : source(0)
- , target(0)
- , data(0)
+ : source(nullptr)
+ , target(nullptr)
+ , data(nullptr)
{ }
QObject *source;
QObject *target;
diff --git a/src/gui/kernel/qevent.cpp b/src/gui/kernel/qevent.cpp
index e7a320f3a4..f327c2d43e 100644
--- a/src/gui/kernel/qevent.cpp
+++ b/src/gui/kernel/qevent.cpp
@@ -3764,13 +3764,13 @@ static inline void formatTouchEvent(QDebug d, const QTouchEvent &t)
static void formatUnicodeString(QDebug d, const QString &s)
{
- d << '"' << hex;
+ d << '"' << Qt::hex;
for (int i = 0; i < s.size(); ++i) {
if (i)
d << ',';
d << "U+" << s.at(i).unicode();
}
- d << dec << '"';
+ d << Qt::dec << '"';
}
static inline void formatInputMethodEvent(QDebug d, const QInputMethodEvent *e)
@@ -3807,8 +3807,8 @@ static inline void formatInputMethodQueryEvent(QDebug d, const QInputMethodQuery
QDebugStateSaver saver(d);
d.noquote();
const Qt::InputMethodQueries queries = e->queries();
- d << "QInputMethodQueryEvent(queries=" << showbase << hex << int(queries)
- << noshowbase << dec << ", {";
+ d << "QInputMethodQueryEvent(queries=" << Qt::showbase << Qt::hex << int(queries)
+ << Qt::noshowbase << Qt::dec << ", {";
for (unsigned mask = 1; mask <= Qt::ImInputItemClipRectangle; mask<<=1) {
if (queries & mask) {
const Qt::InputMethodQuery query = static_cast<Qt::InputMethodQuery>(mask);
@@ -4001,7 +4001,7 @@ QDebug operator<<(QDebug dbg, const QTouchEvent::TouchPoint &tp)
{
QDebugStateSaver saver(dbg);
dbg.nospace();
- dbg << "TouchPoint(" << hex << tp.id() << dec << " (";
+ dbg << "TouchPoint(" << Qt::hex << tp.id() << Qt::dec << " (";
QtDebugUtils::formatQPoint(dbg, tp.pos());
dbg << ") ";
QtDebugUtils::formatQEnum(dbg, tp.state());
@@ -5320,7 +5320,7 @@ QScrollEvent::ScrollState QScrollEvent::scrollState() const
/*!
Creates a new QScreenOrientationChangeEvent
- \a orientation is the new orientation of the screen.
+ \a screenOrientation is the new orientation of the \a screen.
*/
QScreenOrientationChangeEvent::QScreenOrientationChangeEvent(QScreen *screen, Qt::ScreenOrientation screenOrientation)
: QEvent(QEvent::OrientationChange), m_screen(screen), m_orientation(screenOrientation)
@@ -5426,7 +5426,7 @@ QPointingDeviceUniqueId QPointingDeviceUniqueId::fromNumericId(qint64 id)
\sa isValid()
*/
-qint64 QPointingDeviceUniqueId::numericId() const Q_DECL_NOTHROW
+qint64 QPointingDeviceUniqueId::numericId() const noexcept
{
return m_numericId;
}
@@ -5438,7 +5438,7 @@ qint64 QPointingDeviceUniqueId::numericId() const Q_DECL_NOTHROW
Returns whether the two unique pointer IDs \a lhs and \a rhs identify the same pointer
(\c true) or not (\c false).
*/
-bool operator==(QPointingDeviceUniqueId lhs, QPointingDeviceUniqueId rhs) Q_DECL_NOTHROW
+bool operator==(QPointingDeviceUniqueId lhs, QPointingDeviceUniqueId rhs) noexcept
{
return lhs.numericId() == rhs.numericId();
}
@@ -5458,7 +5458,7 @@ bool operator==(QPointingDeviceUniqueId lhs, QPointingDeviceUniqueId rhs) Q_DECL
Returns the hash value for \a key, using \a seed to seed the calculation.
*/
-uint qHash(QPointingDeviceUniqueId key, uint seed) Q_DECL_NOTHROW
+uint qHash(QPointingDeviceUniqueId key, uint seed) noexcept
{
return qHash(key.numericId(), seed);
}
diff --git a/src/gui/kernel/qevent.h b/src/gui/kernel/qevent.h
index 2b1c6a6e31..eb0a6208a9 100644
--- a/src/gui/kernel/qevent.h
+++ b/src/gui/kernel/qevent.h
@@ -549,7 +549,7 @@ public:
};
class Attribute {
public:
- Attribute(AttributeType typ, int s, int l, QVariant val) : type(typ), start(s), length(l), value(qMove(val)) {}
+ Attribute(AttributeType typ, int s, int l, QVariant val) : type(typ), start(s), length(l), value(std::move(val)) {}
Attribute(AttributeType typ, int s, int l) : type(typ), start(s), length(l), value() {}
AttributeType type;
@@ -811,14 +811,14 @@ class Q_GUI_EXPORT QPointingDeviceUniqueId
Q_PROPERTY(qint64 numericId READ numericId CONSTANT)
public:
Q_ALWAYS_INLINE
- Q_DECL_CONSTEXPR QPointingDeviceUniqueId() Q_DECL_NOTHROW : m_numericId(-1) {}
+ Q_DECL_CONSTEXPR QPointingDeviceUniqueId() noexcept : m_numericId(-1) {}
// compiler-generated copy/move ctor/assignment operators are ok!
// compiler-generated dtor is ok!
static QPointingDeviceUniqueId fromNumericId(qint64 id);
- Q_ALWAYS_INLINE Q_DECL_CONSTEXPR bool isValid() const Q_DECL_NOTHROW { return m_numericId != -1; }
- qint64 numericId() const Q_DECL_NOTHROW;
+ Q_ALWAYS_INLINE Q_DECL_CONSTEXPR bool isValid() const noexcept { return m_numericId != -1; }
+ qint64 numericId() const noexcept;
private:
// TODO: for TUIO 2, or any other type of complex token ID, an internal
@@ -829,10 +829,10 @@ private:
Q_DECLARE_TYPEINFO(QPointingDeviceUniqueId, Q_MOVABLE_TYPE);
template <> class QList<QPointingDeviceUniqueId> {}; // to prevent instantiation: use QVector instead
-Q_GUI_EXPORT bool operator==(QPointingDeviceUniqueId lhs, QPointingDeviceUniqueId rhs) Q_DECL_NOTHROW;
-inline bool operator!=(QPointingDeviceUniqueId lhs, QPointingDeviceUniqueId rhs) Q_DECL_NOTHROW
+Q_GUI_EXPORT bool operator==(QPointingDeviceUniqueId lhs, QPointingDeviceUniqueId rhs) noexcept;
+inline bool operator!=(QPointingDeviceUniqueId lhs, QPointingDeviceUniqueId rhs) noexcept
{ return !operator==(lhs, rhs); }
-Q_GUI_EXPORT uint qHash(QPointingDeviceUniqueId key, uint seed = 0) Q_DECL_NOTHROW;
+Q_GUI_EXPORT uint qHash(QPointingDeviceUniqueId key, uint seed = 0) noexcept;
@@ -855,19 +855,17 @@ public:
explicit TouchPoint(int id = -1);
TouchPoint(const TouchPoint &other);
-#ifdef Q_COMPILER_RVALUE_REFS
- TouchPoint(TouchPoint &&other) Q_DECL_NOEXCEPT
+ TouchPoint(TouchPoint &&other) noexcept
: d(nullptr)
{ qSwap(d, other.d); }
- TouchPoint &operator=(TouchPoint &&other) Q_DECL_NOEXCEPT
+ TouchPoint &operator=(TouchPoint &&other) noexcept
{ qSwap(d, other.d); return *this; }
-#endif
~TouchPoint();
TouchPoint &operator=(const TouchPoint &other)
{ if ( d != other.d ) { TouchPoint copy(other); swap(copy); } return *this; }
- void swap(TouchPoint &other) Q_DECL_NOEXCEPT
+ void swap(TouchPoint &other) noexcept
{ qSwap(d, other.d); }
int id() const;
diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp
index e88fcdb55a..7799be77d2 100644
--- a/src/gui/kernel/qguiapplication.cpp
+++ b/src/gui/kernel/qguiapplication.cpp
@@ -68,7 +68,7 @@
#include <qpalette.h>
#include <qscreen.h>
#include "qsessionmanager.h"
-#include <private/qcolorprofile_p.h>
+#include <private/qcolortrclut_p.h>
#include <private/qscreen_p.h>
#include <QtGui/qgenericpluginfactory.h>
@@ -1306,6 +1306,58 @@ static void init_plugins(const QList<QByteArray> &pluginList)
}
}
+void QGuiApplicationPrivate::addQtOptions(QList<QCommandLineOption> *options)
+{
+ QCoreApplicationPrivate::addQtOptions(options);
+
+#if defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN)
+ const QByteArray sessionType = qgetenv("XDG_SESSION_TYPE");
+ const bool x11 = sessionType == "x11";
+ // Technically the x11 aliases are only available if platformName is "xcb", but we can't know that here.
+#else
+ const bool x11 = false;
+#endif
+
+ options->append(QCommandLineOption(QStringLiteral("platform"),
+ QGuiApplication::tr("QPA plugin. See QGuiApplication documentation for available options for each plugin."), QStringLiteral("platformName[:options]")));
+ options->append(QCommandLineOption(QStringLiteral("platformpluginpath"),
+ QGuiApplication::tr("Path to the platform plugins."), QStringLiteral("path")));
+ options->append(QCommandLineOption(QStringLiteral("platformtheme"),
+ QGuiApplication::tr("Platform theme."), QStringLiteral("theme")));
+ options->append(QCommandLineOption(QStringLiteral("plugin"),
+ QGuiApplication::tr("Additional plugins to load, can be specified multiple times."), QStringLiteral("plugin")));
+ options->append(QCommandLineOption(QStringLiteral("qwindowgeometry"),
+ QGuiApplication::tr("Window geometry for the main window, using the X11-syntax, like 100x100+50+50."), QStringLiteral("geometry")));
+ options->append(QCommandLineOption(QStringLiteral("qwindowicon"),
+ QGuiApplication::tr("Default window icon."), QStringLiteral("icon")));
+ options->append(QCommandLineOption(QStringLiteral("qwindowtitle"),
+ QGuiApplication::tr("Title of the first window."), QStringLiteral("title")));
+ options->append(QCommandLineOption(QStringLiteral("reverse"),
+ QGuiApplication::tr("Sets the application's layout direction to Qt::RightToLeft (debugging helper).")));
+ options->append(QCommandLineOption(QStringLiteral("session"),
+ QGuiApplication::tr("Restores the application from an earlier session."), QStringLiteral("session")));
+
+ if (x11) {
+ options->append(QCommandLineOption(QStringLiteral("display"),
+ QGuiApplication::tr("Display name, overrides $DISPLAY."), QStringLiteral("display")));
+ options->append(QCommandLineOption(QStringLiteral("name"),
+ QGuiApplication::tr("Instance name according to ICCCM 4.1.2.5."), QStringLiteral("name")));
+ options->append(QCommandLineOption(QStringLiteral("nograb"),
+ QGuiApplication::tr("Disable mouse grabbing (useful in debuggers).")));
+ options->append(QCommandLineOption(QStringLiteral("dograb"),
+ QGuiApplication::tr("Force mouse grabbing (even when running in a debugger).")));
+ options->append(QCommandLineOption(QStringLiteral("visual"),
+ QGuiApplication::tr("ID of the X11 Visual to use."), QStringLiteral("id")));
+ // Not using the "QStringList names" solution for those aliases, because it makes the first column too wide
+ options->append(QCommandLineOption(QStringLiteral("geometry"),
+ QGuiApplication::tr("Alias for --windowgeometry."), QStringLiteral("geometry")));
+ options->append(QCommandLineOption(QStringLiteral("icon"),
+ QGuiApplication::tr("Alias for --windowicon."), QStringLiteral("icon")));
+ options->append(QCommandLineOption(QStringLiteral("title"),
+ QGuiApplication::tr("Alias for --windowtitle."), QStringLiteral("title")));
+ }
+}
+
void QGuiApplicationPrivate::createPlatformIntegration()
{
QHighDpiScaling::initHighDpiScaling();
@@ -1648,8 +1700,6 @@ QGuiApplicationPrivate::~QGuiApplicationPrivate()
platform_theme = 0;
delete platform_integration;
platform_integration = 0;
- delete m_a8ColorProfile.load();
- delete m_a32ColorProfile.load();
window_list.clear();
screen_list.clear();
@@ -1832,7 +1882,11 @@ bool QGuiApplicationPrivate::sendQWindowEventToQPlatformWindow(QWindow *window,
return platformWindow->windowEvent(event);
}
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+bool QGuiApplicationPrivate::processNativeEvent(QWindow *window, const QByteArray &eventType, void *message, qintptr *result)
+#else
bool QGuiApplicationPrivate::processNativeEvent(QWindow *window, const QByteArray &eventType, void *message, long *result)
+#endif
{
return window->nativeEvent(eventType, message, result);
}
@@ -3990,32 +4044,26 @@ void QGuiApplicationPrivate::notifyDragStarted(const QDrag *drag)
}
#endif
-const QColorProfile *QGuiApplicationPrivate::colorProfileForA8Text()
+const QColorTrcLut *QGuiApplicationPrivate::colorProfileForA8Text()
{
#ifdef Q_OS_WIN
- QColorProfile *result = m_a8ColorProfile.load();
- if (!result){
- QColorProfile *cs = QColorProfile::fromGamma(2.31); // This is a hard-coded thing for Windows text rendering
- if (!m_a8ColorProfile.testAndSetRelease(0, cs))
- delete cs;
- result = m_a8ColorProfile.load();
+ if (!m_a8ColorProfile){
+ QColorTrcLut *cs = QColorTrcLut::fromGamma(2.31); // This is a hard-coded thing for Windows text rendering
+ m_a8ColorProfile.reset(cs);
}
- return result;
+ return m_a8ColorProfile.get();
#else
return colorProfileForA32Text();
#endif
}
-const QColorProfile *QGuiApplicationPrivate::colorProfileForA32Text()
+const QColorTrcLut *QGuiApplicationPrivate::colorProfileForA32Text()
{
- QColorProfile *result = m_a32ColorProfile.load();
- if (!result){
- QColorProfile *cs = QColorProfile::fromGamma(fontSmoothingGamma);
- if (!m_a32ColorProfile.testAndSetRelease(0, cs))
- delete cs;
- result = m_a32ColorProfile.load();
+ if (!m_a32ColorProfile) {
+ QColorTrcLut *cs = QColorTrcLut::fromGamma(fontSmoothingGamma);
+ m_a32ColorProfile.reset(cs);
}
- return result;
+ return m_a32ColorProfile.get();
}
void QGuiApplicationPrivate::_q_updateFocusObject(QObject *object)
diff --git a/src/gui/kernel/qguiapplication_p.h b/src/gui/kernel/qguiapplication_p.h
index 714c72ac08..6902a99eca 100644
--- a/src/gui/kernel/qguiapplication_p.h
+++ b/src/gui/kernel/qguiapplication_p.h
@@ -55,6 +55,7 @@
#include <QtGui/qguiapplication.h>
#include <QtCore/QPointF>
+#include <QtCore/QSharedPointer>
#include <QtCore/private/qcoreapplication_p.h>
#include <QtCore/private/qthread_p.h>
@@ -66,7 +67,7 @@
QT_BEGIN_NAMESPACE
-class QColorProfile;
+class QColorTrcLut;
class QPlatformIntegration;
class QPlatformTheme;
class QPlatformDragQtResponse;
@@ -91,6 +92,7 @@ public:
virtual void notifyLayoutDirectionChange();
virtual void notifyActiveWindowChange(QWindow *previous);
+ void addQtOptions(QList<QCommandLineOption> *options) override;
virtual bool shouldQuit() override;
bool shouldQuitInternal(const QWindowList &processedWindows);
@@ -114,7 +116,7 @@ public:
if (QCoreApplication::instance())
return QCoreApplication::instance()->d_func()->threadData->eventDispatcher.load();
else
- return 0;
+ return nullptr;
}
static void processMouseEvent(QWindowSystemInterfacePrivate::MouseEvent *e);
@@ -171,7 +173,11 @@ public:
Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers);
#endif
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+ static bool processNativeEvent(QWindow *window, const QByteArray &eventType, void *message, qintptr *result);
+#else
static bool processNativeEvent(QWindow *window, const QByteArray &eventType, void *message, long *result);
+#endif
static bool sendQWindowEventToQPlatformWindow(QWindow *window, QEvent *event);
@@ -204,7 +210,7 @@ public:
static void showModalWindow(QWindow *window);
static void hideModalWindow(QWindow *window);
static void updateBlockedStatus(QWindow *window);
- virtual bool isWindowBlocked(QWindow *window, QWindow **blockingWindow = 0) const;
+ virtual bool isWindowBlocked(QWindow *window, QWindow **blockingWindow = nullptr) const;
virtual bool popupActive() { return false; }
static ulong mousePressTime;
@@ -298,8 +304,8 @@ public:
static QInputDeviceManager *inputDeviceManager();
- const QColorProfile *colorProfileForA8Text();
- const QColorProfile *colorProfileForA32Text();
+ const QColorTrcLut *colorProfileForA8Text();
+ const QColorTrcLut *colorProfileForA32Text();
// hook reimplemented in QApplication to apply the QStyle function on the QIcon
virtual QPixmap applyQIconStyleHelper(QIcon::Mode, const QPixmap &basePixmap) const { return basePixmap; }
@@ -326,8 +332,8 @@ private:
static QGuiApplicationPrivate *self;
static QTouchDevice *m_fakeTouchDevice;
static int m_fakeMouseSourcePointId;
- QAtomicPointer<QColorProfile> m_a8ColorProfile;
- QAtomicPointer<QColorProfile> m_a32ColorProfile;
+ QSharedPointer<QColorTrcLut> m_a8ColorProfile;
+ QSharedPointer<QColorTrcLut> m_a32ColorProfile;
bool ownGlobalShareContext;
diff --git a/src/gui/kernel/qhighdpiscaling_p.h b/src/gui/kernel/qhighdpiscaling_p.h
index dfc6abf5ba..91cfbcbfc7 100644
--- a/src/gui/kernel/qhighdpiscaling_p.h
+++ b/src/gui/kernel/qhighdpiscaling_p.h
@@ -413,6 +413,8 @@ public:
static inline QPoint origin(const QPlatformScreen *) { return QPoint(); }
static inline QPoint mapPositionFromNative(const QPoint &pos, const QPlatformScreen *) { return pos; }
static inline QPoint mapPositionToNative(const QPoint &pos, const QPlatformScreen *) { return pos; }
+ static inline QPoint mapPositionToGlobal(const QPoint &pos, const QPoint &windowGlobalPosition, const QWindow *window) { return pos; }
+ static inline QPoint mapPositionFromGlobal(const QPoint &pos, const QPoint &windowGlobalPosition, const QWindow *window) { return pos; }
static inline QDpi logicalDpi() { return QDpi(-1,-1); }
};
diff --git a/src/gui/kernel/qinputdevicemanager.cpp b/src/gui/kernel/qinputdevicemanager.cpp
index 6e4e5a9c93..11442407e1 100644
--- a/src/gui/kernel/qinputdevicemanager.cpp
+++ b/src/gui/kernel/qinputdevicemanager.cpp
@@ -75,13 +75,13 @@ int QInputDeviceManager::deviceCount(DeviceType type) const
int QInputDeviceManagerPrivate::deviceCount(QInputDeviceManager::DeviceType type) const
{
- return m_deviceCount.value(type);
+ return m_deviceCount[type];
}
void QInputDeviceManagerPrivate::setDeviceCount(QInputDeviceManager::DeviceType type, int count)
{
Q_Q(QInputDeviceManager);
- if (m_deviceCount.value(type) != count) {
+ if (m_deviceCount[type] != count) {
m_deviceCount[type] = count;
emit q->deviceListChanged(type);
}
diff --git a/src/gui/kernel/qinputdevicemanager_p.h b/src/gui/kernel/qinputdevicemanager_p.h
index 74494d712b..1d20b102e3 100644
--- a/src/gui/kernel/qinputdevicemanager_p.h
+++ b/src/gui/kernel/qinputdevicemanager_p.h
@@ -69,7 +69,9 @@ public:
DeviceTypePointer,
DeviceTypeKeyboard,
DeviceTypeTouch,
- DeviceTypeTablet
+ DeviceTypeTablet,
+
+ NumDeviceTypes
};
QInputDeviceManager(QObject *parent = nullptr);
diff --git a/src/gui/kernel/qinputdevicemanager_p_p.h b/src/gui/kernel/qinputdevicemanager_p_p.h
index 0a91252fbc..871f9315c3 100644
--- a/src/gui/kernel/qinputdevicemanager_p_p.h
+++ b/src/gui/kernel/qinputdevicemanager_p_p.h
@@ -52,10 +52,11 @@
//
#include <QtGui/private/qtguiglobal_p.h>
-#include <QtCore/qmap.h>
#include <private/qobject_p.h>
#include "qinputdevicemanager_p.h"
+#include <array>
+
QT_BEGIN_NAMESPACE
class Q_GUI_EXPORT QInputDeviceManagerPrivate : public QObjectPrivate
@@ -68,7 +69,7 @@ public:
int deviceCount(QInputDeviceManager::DeviceType type) const;
void setDeviceCount(QInputDeviceManager::DeviceType type, int count);
- QMap<QInputDeviceManager::DeviceType, int> m_deviceCount;
+ std::array<int, QInputDeviceManager::NumDeviceTypes> m_deviceCount;
Qt::KeyboardModifiers keyboardModifiers;
};
diff --git a/src/gui/kernel/qinputmethod_p.h b/src/gui/kernel/qinputmethod_p.h
index 0c2b739d92..5657edeb4e 100644
--- a/src/gui/kernel/qinputmethod_p.h
+++ b/src/gui/kernel/qinputmethod_p.h
@@ -67,7 +67,7 @@ class QInputMethodPrivate : public QObjectPrivate
Q_DECLARE_PUBLIC(QInputMethod)
public:
- inline QInputMethodPrivate() : testContext(0)
+ inline QInputMethodPrivate() : testContext(nullptr)
{}
QPlatformInputContext *platformInputContext() const
{
diff --git a/src/gui/kernel/qkeysequence.cpp b/src/gui/kernel/qkeysequence.cpp
index 74bf6e6b47..8688bb8403 100644
--- a/src/gui/kernel/qkeysequence.cpp
+++ b/src/gui/kernel/qkeysequence.cpp
@@ -46,9 +46,6 @@
#include "qdebug.h"
#include <QtCore/qhashfunctions.h>
-#ifndef QT_NO_REGEXP
-# include "qregexp.h"
-#endif
#ifndef QT_NO_DATASTREAM
# include "qdatastream.h"
#endif
@@ -1466,7 +1463,7 @@ bool QKeySequence::operator==(const QKeySequence &other) const
Calculates the hash value of \a key, using
\a seed to seed the calculation.
*/
-uint qHash(const QKeySequence &key, uint seed) Q_DECL_NOTHROW
+uint qHash(const QKeySequence &key, uint seed) noexcept
{
return qHashRange(key.d->key, key.d->key + QKeySequencePrivate::MaxKeyCount, seed);
}
diff --git a/src/gui/kernel/qkeysequence.h b/src/gui/kernel/qkeysequence.h
index 65f3a93d9c..3dcbbe5941 100644
--- a/src/gui/kernel/qkeysequence.h
+++ b/src/gui/kernel/qkeysequence.h
@@ -66,7 +66,7 @@ void qt_set_sequence_auto_mnemonic(bool b);
class QVariant;
class QKeySequencePrivate;
-Q_GUI_EXPORT Q_DECL_PURE_FUNCTION uint qHash(const QKeySequence &key, uint seed = 0) Q_DECL_NOTHROW;
+Q_GUI_EXPORT Q_DECL_PURE_FUNCTION uint qHash(const QKeySequence &key, uint seed = 0) noexcept;
class Q_GUI_EXPORT QKeySequence
{
@@ -186,10 +186,8 @@ public:
operator QVariant() const;
int operator[](uint i) const;
QKeySequence &operator=(const QKeySequence &other);
-#ifdef Q_COMPILER_RVALUE_REFS
- QKeySequence &operator=(QKeySequence &&other) Q_DECL_NOTHROW { swap(other); return *this; }
-#endif
- void swap(QKeySequence &other) Q_DECL_NOTHROW { qSwap(d, other.d); }
+ QKeySequence &operator=(QKeySequence &&other) noexcept { swap(other); return *this; }
+ void swap(QKeySequence &other) noexcept { qSwap(d, other.d); }
bool operator==(const QKeySequence &other) const;
inline bool operator!= (const QKeySequence &other) const
@@ -214,7 +212,7 @@ private:
friend Q_GUI_EXPORT QDataStream &operator<<(QDataStream &in, const QKeySequence &ks);
friend Q_GUI_EXPORT QDataStream &operator>>(QDataStream &in, QKeySequence &ks);
- friend Q_GUI_EXPORT uint qHash(const QKeySequence &key, uint seed) Q_DECL_NOTHROW;
+ friend Q_GUI_EXPORT uint qHash(const QKeySequence &key, uint seed) noexcept;
friend class QShortcutMap;
friend class QShortcut;
diff --git a/src/gui/kernel/qopenglcontext_p.h b/src/gui/kernel/qopenglcontext_p.h
index 2849d0c58e..c6ad893ee6 100644
--- a/src/gui/kernel/qopenglcontext_p.h
+++ b/src/gui/kernel/qopenglcontext_p.h
@@ -131,7 +131,7 @@ class Q_GUI_EXPORT QOpenGLContextGroupPrivate : public QObjectPrivate
Q_DECLARE_PUBLIC(QOpenGLContextGroup)
public:
QOpenGLContextGroupPrivate()
- : m_context(0)
+ : m_context(nullptr)
, m_mutex(QMutex::Recursive)
, m_refs(0)
{
@@ -198,20 +198,20 @@ class Q_GUI_EXPORT QOpenGLContextPrivate : public QObjectPrivate
Q_DECLARE_PUBLIC(QOpenGLContext)
public:
QOpenGLContextPrivate()
- : qGLContextHandle(0)
- , qGLContextDeleteFunction(0)
- , platformGLContext(0)
- , shareContext(0)
- , shareGroup(0)
- , screen(0)
- , surface(0)
- , functions(0)
- , textureFunctions(0)
+ : qGLContextHandle(nullptr)
+ , qGLContextDeleteFunction(nullptr)
+ , platformGLContext(nullptr)
+ , shareContext(nullptr)
+ , shareGroup(nullptr)
+ , screen(nullptr)
+ , surface(nullptr)
+ , functions(nullptr)
+ , textureFunctions(nullptr)
, max_texture_size(-1)
, workaround_brokenFBOReadBack(false)
, workaround_brokenTexSubImage(false)
, workaround_missingPrecisionQualifiers(false)
- , active_engine(0)
+ , active_engine(nullptr)
, qgl_current_fbo_invalid(false)
, qgl_current_fbo(nullptr)
, defaultFboRedirect(0)
diff --git a/src/gui/kernel/qpalette.cpp b/src/gui/kernel/qpalette.cpp
index 193fd9590d..90471e15e1 100644
--- a/src/gui/kernel/qpalette.cpp
+++ b/src/gui/kernel/qpalette.cpp
@@ -1209,7 +1209,7 @@ QDebug operator<<(QDebug dbg, const QPalette &p)
QDebugStateSaver saver(dbg);
QDebug nospace = dbg.nospace();
const uint mask = p.resolve();
- nospace << "QPalette(resolve=" << hex << showbase << mask << ',';
+ nospace << "QPalette(resolve=" << Qt::hex << Qt::showbase << mask << ',';
for (int role = 0; role < (int)QPalette::NColorRoles; ++role) {
if (mask & (1<<role)) {
if (role)
@@ -1225,7 +1225,7 @@ QDebug operator<<(QDebug dbg, const QPalette &p)
nospace << ']';
}
}
- nospace << ')' << noshowbase << dec;
+ nospace << ')' << Qt::noshowbase << Qt::dec;
return dbg;
}
#endif
diff --git a/src/gui/kernel/qpalette.h b/src/gui/kernel/qpalette.h
index e931e01480..d3a840d9ad 100644
--- a/src/gui/kernel/qpalette.h
+++ b/src/gui/kernel/qpalette.h
@@ -67,18 +67,16 @@ public:
QPalette(const QPalette &palette);
~QPalette();
QPalette &operator=(const QPalette &palette);
-#ifdef Q_COMPILER_RVALUE_REFS
- QPalette(QPalette &&other) Q_DECL_NOTHROW
+ QPalette(QPalette &&other) noexcept
: d(other.d), data(other.data)
{ other.d = nullptr; }
- inline QPalette &operator=(QPalette &&other) Q_DECL_NOEXCEPT
+ inline QPalette &operator=(QPalette &&other) noexcept
{
for_faster_swapping_dont_use = other.for_faster_swapping_dont_use;
qSwap(d, other.d); return *this;
}
-#endif
- void swap(QPalette &other) Q_DECL_NOEXCEPT
+ void swap(QPalette &other) noexcept
{
qSwap(d, other.d);
qSwap(for_faster_swapping_dont_use, other.for_faster_swapping_dont_use);
diff --git a/src/gui/kernel/qpixelformat.h b/src/gui/kernel/qpixelformat.h
index 6b2d6a6ac7..a041a39fc1 100644
--- a/src/gui/kernel/qpixelformat.h
+++ b/src/gui/kernel/qpixelformat.h
@@ -95,7 +95,7 @@ class QPixelFormat
Q_STATIC_ASSERT(uint(TotalFieldWidthByWidths) == uint(TotalFieldWidthByOffsets));
Q_STATIC_ASSERT(uint(TotalFieldWidthByWidths) == 8 * sizeof(quint64));
- Q_DECL_CONSTEXPR inline uchar get(Field offset, FieldWidth width) const Q_DECL_NOTHROW
+ Q_DECL_CONSTEXPR inline uchar get(Field offset, FieldWidth width) const noexcept
{ return uchar((data >> uint(offset)) & ((Q_UINT64_C(1) << uint(width)) - Q_UINT64_C(1))); }
Q_DECL_CONSTEXPR static inline quint64 set(Field offset, FieldWidth width, uchar value)
{ return (quint64(value) & ((Q_UINT64_C(1) << uint(width)) - Q_UINT64_C(1))) << uint(offset); }
@@ -160,7 +160,7 @@ public:
CurrentSystemEndian
};
- Q_DECL_CONSTEXPR inline QPixelFormat() Q_DECL_NOTHROW : data(0) {}
+ Q_DECL_CONSTEXPR inline QPixelFormat() noexcept : data(0) {}
Q_DECL_CONSTEXPR inline QPixelFormat(ColorModel colorModel,
uchar firstSize,
uchar secondSize,
@@ -173,47 +173,47 @@ public:
AlphaPremultiplied premultiplied,
TypeInterpretation typeInterpretation,
ByteOrder byteOrder = CurrentSystemEndian,
- uchar subEnum = 0) Q_DECL_NOTHROW;
+ uchar subEnum = 0) noexcept;
- Q_DECL_CONSTEXPR inline ColorModel colorModel() const Q_DECL_NOTHROW { return ColorModel(get(ModelField, ModelFieldWidth)); }
- Q_DECL_CONSTEXPR inline uchar channelCount() const Q_DECL_NOTHROW { return (get(FirstField, FirstFieldWidth) > 0) +
+ Q_DECL_CONSTEXPR inline ColorModel colorModel() const noexcept { return ColorModel(get(ModelField, ModelFieldWidth)); }
+ Q_DECL_CONSTEXPR inline uchar channelCount() const noexcept { return (get(FirstField, FirstFieldWidth) > 0) +
(get(SecondField, SecondFieldWidth) > 0) +
(get(ThirdField, ThirdFieldWidth) > 0) +
(get(FourthField, FourthFieldWidth) > 0) +
(get(FifthField, FifthFieldWidth) > 0) +
(get(AlphaField, AlphaFieldWidth) > 0); }
- Q_DECL_CONSTEXPR inline uchar redSize() const Q_DECL_NOTHROW { return get(FirstField, FirstFieldWidth); }
- Q_DECL_CONSTEXPR inline uchar greenSize() const Q_DECL_NOTHROW { return get(SecondField, SecondFieldWidth); }
- Q_DECL_CONSTEXPR inline uchar blueSize() const Q_DECL_NOTHROW { return get(ThirdField, ThirdFieldWidth); }
+ Q_DECL_CONSTEXPR inline uchar redSize() const noexcept { return get(FirstField, FirstFieldWidth); }
+ Q_DECL_CONSTEXPR inline uchar greenSize() const noexcept { return get(SecondField, SecondFieldWidth); }
+ Q_DECL_CONSTEXPR inline uchar blueSize() const noexcept { return get(ThirdField, ThirdFieldWidth); }
- Q_DECL_CONSTEXPR inline uchar cyanSize() const Q_DECL_NOTHROW { return get(FirstField, FirstFieldWidth); }
- Q_DECL_CONSTEXPR inline uchar magentaSize() const Q_DECL_NOTHROW { return get(SecondField, SecondFieldWidth); }
- Q_DECL_CONSTEXPR inline uchar yellowSize() const Q_DECL_NOTHROW { return get(ThirdField, ThirdFieldWidth); }
- Q_DECL_CONSTEXPR inline uchar blackSize() const Q_DECL_NOTHROW { return get(FourthField, FourthFieldWidth); }
+ Q_DECL_CONSTEXPR inline uchar cyanSize() const noexcept { return get(FirstField, FirstFieldWidth); }
+ Q_DECL_CONSTEXPR inline uchar magentaSize() const noexcept { return get(SecondField, SecondFieldWidth); }
+ Q_DECL_CONSTEXPR inline uchar yellowSize() const noexcept { return get(ThirdField, ThirdFieldWidth); }
+ Q_DECL_CONSTEXPR inline uchar blackSize() const noexcept { return get(FourthField, FourthFieldWidth); }
- Q_DECL_CONSTEXPR inline uchar hueSize() const Q_DECL_NOTHROW { return get(FirstField, FirstFieldWidth); }
- Q_DECL_CONSTEXPR inline uchar saturationSize() const Q_DECL_NOTHROW { return get(SecondField, SecondFieldWidth); }
- Q_DECL_CONSTEXPR inline uchar lightnessSize() const Q_DECL_NOTHROW { return get(ThirdField, ThirdFieldWidth); }
- Q_DECL_CONSTEXPR inline uchar brightnessSize() const Q_DECL_NOTHROW { return get(ThirdField, ThirdFieldWidth); }
+ Q_DECL_CONSTEXPR inline uchar hueSize() const noexcept { return get(FirstField, FirstFieldWidth); }
+ Q_DECL_CONSTEXPR inline uchar saturationSize() const noexcept { return get(SecondField, SecondFieldWidth); }
+ Q_DECL_CONSTEXPR inline uchar lightnessSize() const noexcept { return get(ThirdField, ThirdFieldWidth); }
+ Q_DECL_CONSTEXPR inline uchar brightnessSize() const noexcept { return get(ThirdField, ThirdFieldWidth); }
- Q_DECL_CONSTEXPR inline uchar alphaSize() const Q_DECL_NOTHROW { return get(AlphaField, AlphaFieldWidth); }
+ Q_DECL_CONSTEXPR inline uchar alphaSize() const noexcept { return get(AlphaField, AlphaFieldWidth); }
- Q_DECL_CONSTEXPR inline uchar bitsPerPixel() const Q_DECL_NOTHROW { return get(FirstField, FirstFieldWidth) +
+ Q_DECL_CONSTEXPR inline uchar bitsPerPixel() const noexcept { return get(FirstField, FirstFieldWidth) +
get(SecondField, SecondFieldWidth) +
get(ThirdField, ThirdFieldWidth) +
get(FourthField, FourthFieldWidth) +
get(FifthField, FifthFieldWidth) +
get(AlphaField, AlphaFieldWidth); }
- Q_DECL_CONSTEXPR inline AlphaUsage alphaUsage() const Q_DECL_NOTHROW { return AlphaUsage(get(AlphaUsageField, AlphaUsageFieldWidth)); }
- Q_DECL_CONSTEXPR inline AlphaPosition alphaPosition() const Q_DECL_NOTHROW { return AlphaPosition(get(AlphaPositionField, AlphaPositionFieldWidth)); }
- Q_DECL_CONSTEXPR inline AlphaPremultiplied premultiplied() const Q_DECL_NOTHROW { return AlphaPremultiplied(get(PremulField, PremulFieldWidth)); }
- Q_DECL_CONSTEXPR inline TypeInterpretation typeInterpretation() const Q_DECL_NOTHROW { return TypeInterpretation(get(TypeInterpretationField, TypeInterpretationFieldWidth)); }
- Q_DECL_CONSTEXPR inline ByteOrder byteOrder() const Q_DECL_NOTHROW { return ByteOrder(get(ByteOrderField, ByteOrderFieldWidth)); }
+ Q_DECL_CONSTEXPR inline AlphaUsage alphaUsage() const noexcept { return AlphaUsage(get(AlphaUsageField, AlphaUsageFieldWidth)); }
+ Q_DECL_CONSTEXPR inline AlphaPosition alphaPosition() const noexcept { return AlphaPosition(get(AlphaPositionField, AlphaPositionFieldWidth)); }
+ Q_DECL_CONSTEXPR inline AlphaPremultiplied premultiplied() const noexcept { return AlphaPremultiplied(get(PremulField, PremulFieldWidth)); }
+ Q_DECL_CONSTEXPR inline TypeInterpretation typeInterpretation() const noexcept { return TypeInterpretation(get(TypeInterpretationField, TypeInterpretationFieldWidth)); }
+ Q_DECL_CONSTEXPR inline ByteOrder byteOrder() const noexcept { return ByteOrder(get(ByteOrderField, ByteOrderFieldWidth)); }
- Q_DECL_CONSTEXPR inline YUVLayout yuvLayout() const Q_DECL_NOTHROW { return YUVLayout(get(SubEnumField, SubEnumFieldWidth)); }
- Q_DECL_CONSTEXPR inline uchar subEnum() const Q_DECL_NOTHROW { return get(SubEnumField, SubEnumFieldWidth); }
+ Q_DECL_CONSTEXPR inline YUVLayout yuvLayout() const noexcept { return YUVLayout(get(SubEnumField, SubEnumFieldWidth)); }
+ Q_DECL_CONSTEXPR inline uchar subEnum() const noexcept { return get(SubEnumField, SubEnumFieldWidth); }
private:
Q_DECL_CONSTEXPR static inline ByteOrder resolveByteOrder(ByteOrder bo)
@@ -255,7 +255,7 @@ QPixelFormat::QPixelFormat(ColorModel mdl,
AlphaPremultiplied premult,
TypeInterpretation typeInterp,
ByteOrder b_order,
- uchar s_enum) Q_DECL_NOTHROW
+ uchar s_enum) noexcept
: data(set(ModelField, ModelFieldWidth, uchar(mdl)) |
set(FirstField, FirstFieldWidth, firstSize) |
set(SecondField, SecondFieldWidth, secondSize) |
@@ -280,7 +280,7 @@ Q_DECL_CONSTEXPR inline QPixelFormat qPixelFormatRgba(uchar red,
QPixelFormat::AlphaUsage usage,
QPixelFormat::AlphaPosition position,
QPixelFormat::AlphaPremultiplied pmul=QPixelFormat::NotPremultiplied,
- QPixelFormat::TypeInterpretation typeInt=QPixelFormat::UnsignedInteger) Q_DECL_NOTHROW
+ QPixelFormat::TypeInterpretation typeInt=QPixelFormat::UnsignedInteger) noexcept
{
return QPixelFormat(QPixelFormat::RGB,
red,
@@ -296,7 +296,7 @@ Q_DECL_CONSTEXPR inline QPixelFormat qPixelFormatRgba(uchar red,
}
Q_DECL_CONSTEXPR inline QPixelFormat qPixelFormatGrayscale(uchar channelSize,
- QPixelFormat::TypeInterpretation typeInt=QPixelFormat::UnsignedInteger) Q_DECL_NOTHROW
+ QPixelFormat::TypeInterpretation typeInt=QPixelFormat::UnsignedInteger) noexcept
{
return QPixelFormat(QPixelFormat::Grayscale,
channelSize,
@@ -312,7 +312,7 @@ Q_DECL_CONSTEXPR inline QPixelFormat qPixelFormatGrayscale(uchar channelSize,
}
Q_DECL_CONSTEXPR inline QPixelFormat qPixelFormatAlpha(uchar channelSize,
- QPixelFormat::TypeInterpretation typeInt=QPixelFormat::UnsignedInteger) Q_DECL_NOTHROW
+ QPixelFormat::TypeInterpretation typeInt=QPixelFormat::UnsignedInteger) noexcept
{
return QPixelFormat(QPixelFormat::Alpha,
0,
@@ -331,7 +331,7 @@ Q_DECL_CONSTEXPR inline QPixelFormat qPixelFormatCmyk(uchar channelSize,
uchar alfa=0,
QPixelFormat::AlphaUsage usage=QPixelFormat::IgnoresAlpha,
QPixelFormat::AlphaPosition position=QPixelFormat::AtBeginning,
- QPixelFormat::TypeInterpretation typeInt=QPixelFormat::UnsignedInteger) Q_DECL_NOTHROW
+ QPixelFormat::TypeInterpretation typeInt=QPixelFormat::UnsignedInteger) noexcept
{
return QPixelFormat(QPixelFormat::CMYK,
channelSize,
@@ -350,7 +350,7 @@ Q_DECL_CONSTEXPR inline QPixelFormat qPixelFormatHsl(uchar channelSize,
uchar alfa=0,
QPixelFormat::AlphaUsage usage=QPixelFormat::IgnoresAlpha,
QPixelFormat::AlphaPosition position=QPixelFormat::AtBeginning,
- QPixelFormat::TypeInterpretation typeInt=QPixelFormat::FloatingPoint) Q_DECL_NOTHROW
+ QPixelFormat::TypeInterpretation typeInt=QPixelFormat::FloatingPoint) noexcept
{
return QPixelFormat(QPixelFormat::HSL,
channelSize,
@@ -369,7 +369,7 @@ Q_DECL_CONSTEXPR inline QPixelFormat qPixelFormatHsv(uchar channelSize,
uchar alfa=0,
QPixelFormat::AlphaUsage usage=QPixelFormat::IgnoresAlpha,
QPixelFormat::AlphaPosition position=QPixelFormat::AtBeginning,
- QPixelFormat::TypeInterpretation typeInt=QPixelFormat::FloatingPoint) Q_DECL_NOTHROW
+ QPixelFormat::TypeInterpretation typeInt=QPixelFormat::FloatingPoint) noexcept
{
return QPixelFormat(QPixelFormat::HSV,
channelSize,
diff --git a/src/gui/kernel/qplatformdialoghelper.cpp b/src/gui/kernel/qplatformdialoghelper.cpp
index d14d575056..4bee153489 100644
--- a/src/gui/kernel/qplatformdialoghelper.cpp
+++ b/src/gui/kernel/qplatformdialoghelper.cpp
@@ -41,7 +41,9 @@
#include <QtCore/QCoreApplication>
#include <QtCore/QVariant>
+#if QT_CONFIG(regularexpression)
#include <QtCore/QRegularExpression>
+#endif
#include <QtCore/QSharedData>
#if QT_CONFIG(settings)
#include <QtCore/QSettings>
@@ -786,6 +788,7 @@ const char QPlatformFileDialogHelper::filterRegExp[] =
// Makes a list of filters from a normal filter string "Image Files (*.png *.jpg)"
QStringList QPlatformFileDialogHelper::cleanFilterList(const QString &filter)
{
+#if QT_CONFIG(regularexpression)
QRegularExpression regexp(QString::fromLatin1(filterRegExp));
Q_ASSERT(regexp.isValid());
QString f = filter;
@@ -794,6 +797,9 @@ QStringList QPlatformFileDialogHelper::cleanFilterList(const QString &filter)
if (match.hasMatch())
f = match.captured(2);
return f.split(QLatin1Char(' '), QString::SkipEmptyParts);
+#else
+ return QStringList();
+#endif
}
// Message dialog
diff --git a/src/gui/kernel/qplatformdialoghelper.h b/src/gui/kernel/qplatformdialoghelper.h
index f09bec12da..ba800a696f 100644
--- a/src/gui/kernel/qplatformdialoghelper.h
+++ b/src/gui/kernel/qplatformdialoghelper.h
@@ -318,7 +318,9 @@ public:
ShowDirsOnly = 0x00000001,
DontResolveSymlinks = 0x00000002,
DontConfirmOverwrite = 0x00000004,
- DontUseSheet = 0x00000008,
+#if QT_DEPRECATED_SINCE(5, 14)
+ DontUseSheet Q_DECL_ENUMERATOR_DEPRECATED = 0x00000008,
+#endif
DontUseNativeDialog = 0x00000010,
ReadOnly = 0x00000020,
HideNameFilterDetails = 0x00000040,
diff --git a/src/gui/kernel/qplatformintegration.h b/src/gui/kernel/qplatformintegration.h
index 389b35dbc0..b764bd2617 100644
--- a/src/gui/kernel/qplatformintegration.h
+++ b/src/gui/kernel/qplatformintegration.h
@@ -114,7 +114,7 @@ public:
virtual QPlatformPixmap *createPlatformPixmap(QPlatformPixmap::PixelType type) const;
virtual QPlatformWindow *createPlatformWindow(QWindow *window) const = 0;
- virtual QPlatformWindow *createForeignWindow(QWindow *, WId) const { return 0; }
+ virtual QPlatformWindow *createForeignWindow(QWindow *, WId) const { return nullptr; }
virtual QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const = 0;
#ifndef QT_NO_OPENGL
virtual QPlatformOpenGLContext *createPlatformOpenGLContext(QOpenGLContext *context) const;
diff --git a/src/gui/kernel/qplatformtheme.h b/src/gui/kernel/qplatformtheme.h
index 54c8c70025..356c4ea3ea 100644
--- a/src/gui/kernel/qplatformtheme.h
+++ b/src/gui/kernel/qplatformtheme.h
@@ -309,7 +309,7 @@ public:
virtual QPixmap standardPixmap(StandardPixmap sp, const QSizeF &size) const;
virtual QIcon fileIcon(const QFileInfo &fileInfo,
- QPlatformTheme::IconOptions iconOptions = 0) const;
+ QPlatformTheme::IconOptions iconOptions = nullptr) const;
virtual QIconEngine *createIconEngine(const QString &iconName) const;
#ifndef QT_NO_SHORTCUT
diff --git a/src/gui/kernel/qscreen.cpp b/src/gui/kernel/qscreen.cpp
index 952023dd1b..c79f597414 100644
--- a/src/gui/kernel/qscreen.cpp
+++ b/src/gui/kernel/qscreen.cpp
@@ -768,7 +768,7 @@ QPixmap QScreen::grabWindow(WId window, int x, int y, int width, int height)
static inline void formatRect(QDebug &debug, const QRect r)
{
debug << r.width() << 'x' << r.height()
- << forcesign << r.x() << r.y() << noforcesign;
+ << Qt::forcesign << r.x() << r.y() << Qt::noforcesign;
}
Q_GUI_EXPORT QDebug operator<<(QDebug debug, const QScreen *screen)
diff --git a/src/gui/kernel/qscreen_p.h b/src/gui/kernel/qscreen_p.h
index f31658355b..e5988ff829 100644
--- a/src/gui/kernel/qscreen_p.h
+++ b/src/gui/kernel/qscreen_p.h
@@ -64,12 +64,6 @@ class QScreenPrivate : public QObjectPrivate
{
Q_DECLARE_PUBLIC(QScreen)
public:
- QScreenPrivate()
- : platformScreen(0)
- , orientationUpdateMask(0)
- {
- }
-
void setPlatformScreen(QPlatformScreen *screen);
void updateHighDpi()
{
@@ -79,16 +73,16 @@ public:
void updatePrimaryOrientation();
- QPlatformScreen *platformScreen;
+ QPlatformScreen *platformScreen = nullptr;
Qt::ScreenOrientations orientationUpdateMask;
- Qt::ScreenOrientation orientation;
- Qt::ScreenOrientation filteredOrientation;
- Qt::ScreenOrientation primaryOrientation;
+ Qt::ScreenOrientation orientation = Qt::PrimaryOrientation;
+ Qt::ScreenOrientation filteredOrientation = Qt::PrimaryOrientation;
+ Qt::ScreenOrientation primaryOrientation = Qt::LandscapeOrientation;
QRect geometry;
QRect availableGeometry;
- QDpi logicalDpi;
- qreal refreshRate;
+ QDpi logicalDpi = {96, 96};
+ qreal refreshRate = 60;
};
QT_END_NAMESPACE
diff --git a/src/gui/kernel/qsessionmanager.cpp b/src/gui/kernel/qsessionmanager.cpp
index 493a321c74..e5e9c624b2 100644
--- a/src/gui/kernel/qsessionmanager.cpp
+++ b/src/gui/kernel/qsessionmanager.cpp
@@ -123,7 +123,11 @@ QSessionManagerPrivate::QSessionManagerPrivate(const QString &id,
const QString &key)
: QObjectPrivate()
{
- platformSessionManager = QGuiApplicationPrivate::platformIntegration()->createPlatformSessionManager(id, key);
+ if (qApp->testAttribute(Qt::AA_DisableSessionManager)) {
+ platformSessionManager = new QPlatformSessionManager(id, key);
+ } else {
+ platformSessionManager = QGuiApplicationPrivate::platformIntegration()->createPlatformSessionManager(id, key);
+ }
Q_ASSERT_X(platformSessionManager, "Platform session management",
"No platform session management, should use the default implementation");
}
diff --git a/src/gui/kernel/qshapedpixmapdndwindow_p.h b/src/gui/kernel/qshapedpixmapdndwindow_p.h
index d9a6ea4888..5089be7284 100644
--- a/src/gui/kernel/qshapedpixmapdndwindow_p.h
+++ b/src/gui/kernel/qshapedpixmapdndwindow_p.h
@@ -63,7 +63,7 @@ class QShapedPixmapWindow : public QRasterWindow
{
Q_OBJECT
public:
- explicit QShapedPixmapWindow(QScreen *screen = 0);
+ explicit QShapedPixmapWindow(QScreen *screen = nullptr);
~QShapedPixmapWindow();
void setUseCompositing(bool on) { m_useCompositing = on; }
diff --git a/src/gui/kernel/qsimpledrag.cpp b/src/gui/kernel/qsimpledrag.cpp
index 9aab332ef5..bba2a863a9 100644
--- a/src/gui/kernel/qsimpledrag.cpp
+++ b/src/gui/kernel/qsimpledrag.cpp
@@ -48,7 +48,6 @@
#include "qpoint.h"
#include "qbuffer.h"
#include "qimage.h"
-#include "qregexp.h"
#include "qdir.h"
#include "qimagereader.h"
#include "qimagewriter.h"
diff --git a/src/gui/kernel/qwindow.cpp b/src/gui/kernel/qwindow.cpp
index ed516c0eed..b71a0c54aa 100644
--- a/src/gui/kernel/qwindow.cpp
+++ b/src/gui/kernel/qwindow.cpp
@@ -2535,7 +2535,12 @@ void QWindow::tabletEvent(QTabletEvent *ev)
Should return true only if the event was handled.
*/
+
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+bool QWindow::nativeEvent(const QByteArray &eventType, void *message, qintptr *result)
+#else
bool QWindow::nativeEvent(const QByteArray &eventType, void *message, long *result)
+#endif
{
Q_UNUSED(eventType);
Q_UNUSED(message);
@@ -2844,13 +2849,13 @@ QDebug operator<<(QDebug debug, const QWindow *window)
if (window->isTopLevel())
debug << ", toplevel";
debug << ", " << geometry.width() << 'x' << geometry.height()
- << forcesign << geometry.x() << geometry.y() << noforcesign;
+ << Qt::forcesign << geometry.x() << geometry.y() << Qt::noforcesign;
const QMargins margins = window->frameMargins();
if (!margins.isNull())
debug << ", margins=" << margins;
debug << ", devicePixelRatio=" << window->devicePixelRatio();
if (const QPlatformWindow *platformWindow = window->handle())
- debug << ", winId=0x" << hex << platformWindow->winId() << dec;
+ debug << ", winId=0x" << Qt::hex << platformWindow->winId() << Qt::dec;
if (const QScreen *screen = window->screen())
debug << ", on " << screen->name();
}
diff --git a/src/gui/kernel/qwindow.h b/src/gui/kernel/qwindow.h
index 1be3c845fe..5ee1d00f5b 100644
--- a/src/gui/kernel/qwindow.h
+++ b/src/gui/kernel/qwindow.h
@@ -364,7 +364,11 @@ protected:
#if QT_CONFIG(tabletevent)
virtual void tabletEvent(QTabletEvent *);
#endif
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+ virtual bool nativeEvent(const QByteArray &eventType, void *message, qintptr *result);
+#else
virtual bool nativeEvent(const QByteArray &eventType, void *message, long *result);
+#endif
QWindow(QWindowPrivate &dd, QWindow *parent);
diff --git a/src/gui/kernel/qwindow_p.h b/src/gui/kernel/qwindow_p.h
index 5367d93e84..5a7ec518fd 100644
--- a/src/gui/kernel/qwindow_p.h
+++ b/src/gui/kernel/qwindow_p.h
@@ -77,50 +77,15 @@ public:
QWindowPrivate()
: QObjectPrivate()
- , surfaceType(QWindow::RasterSurface)
- , windowFlags(Qt::Window)
- , parentWindow(0)
- , platformWindow(0)
- , visible(false)
- , visibilityOnDestroy(false)
- , exposed(false)
- , windowState(Qt::WindowNoState)
- , visibility(QWindow::Hidden)
- , resizeEventPending(true)
- , receivedExpose(false)
- , positionPolicy(WindowFrameExclusive)
- , positionAutomatic(true)
- , resizeAutomatic(true)
- , contentOrientation(Qt::PrimaryOrientation)
- , opacity(qreal(1.0))
- , minimumSize(0, 0)
- , maximumSize(QWINDOWSIZE_MAX, QWINDOWSIZE_MAX)
- , modality(Qt::NonModal)
- , blockedByModalWindow(false)
- , updateRequestPending(false)
- , transientParent(0)
- , topLevelScreen(0)
-#ifndef QT_NO_CURSOR
- , cursor(Qt::ArrowCursor)
- , hasCursor(false)
-#endif
- , compositing(false)
-#if QT_CONFIG(vulkan)
- , vulkanInstance(nullptr)
-#endif
{
isWindow = true;
}
- ~QWindowPrivate()
- {
- }
-
void init(QScreen *targetScreen = nullptr);
void maybeQuitOnLastWindowClosed();
#ifndef QT_NO_CURSOR
- void setCursor(const QCursor *c = 0);
+ void setCursor(const QCursor *c = nullptr);
bool applyCursor();
#endif
@@ -164,57 +129,60 @@ public:
static Qt::WindowState effectiveState(Qt::WindowStates);
- QWindow::SurfaceType surfaceType;
- Qt::WindowFlags windowFlags;
- QWindow *parentWindow;
- QPlatformWindow *platformWindow;
- bool visible;
- bool visibilityOnDestroy;
- bool exposed;
+ // ### Qt6: unused
+ virtual bool allowClickThrough(const QPoint &) const { return true; }
+
+ QWindow::SurfaceType surfaceType = QWindow::RasterSurface;
+ Qt::WindowFlags windowFlags = Qt::Window;
+ QWindow *parentWindow = nullptr;
+ QPlatformWindow *platformWindow = nullptr;
+ bool visible= false;
+ bool visibilityOnDestroy = false;
+ bool exposed = false;
QSurfaceFormat requestedFormat;
QString windowTitle;
QString windowFilePath;
QIcon windowIcon;
QRect geometry;
- Qt::WindowStates windowState;
- QWindow::Visibility visibility;
- bool resizeEventPending;
- bool receivedExpose;
- PositionPolicy positionPolicy;
- bool positionAutomatic;
+ Qt::WindowStates windowState = Qt::WindowNoState;
+ QWindow::Visibility visibility = QWindow::Hidden;
+ bool resizeEventPending = true;
+ bool receivedExpose = false;
+ PositionPolicy positionPolicy = WindowFrameExclusive;
+ bool positionAutomatic = true;
// resizeAutomatic suppresses resizing by QPlatformWindow::initialGeometry().
// It also indicates that width/height=0 is acceptable (for example, for
// the QRollEffect widget) and is thus not cleared in setGeometry().
// An alternative approach might be using -1,-1 as a default size.
- bool resizeAutomatic;
- Qt::ScreenOrientation contentOrientation;
- qreal opacity;
+ bool resizeAutomatic = true;
+ Qt::ScreenOrientation contentOrientation = Qt::PrimaryOrientation;
+ qreal opacity= 1;
QRegion mask;
- QSize minimumSize;
- QSize maximumSize;
+ QSize minimumSize = {0, 0};
+ QSize maximumSize = {QWINDOWSIZE_MAX, QWINDOWSIZE_MAX};
QSize baseSize;
QSize sizeIncrement;
- Qt::WindowModality modality;
- bool blockedByModalWindow;
+ Qt::WindowModality modality = Qt::NonModal;
+ bool blockedByModalWindow = false;
- bool updateRequestPending;
+ bool updateRequestPending = false;
bool transientParentPropertySet = false;
QPointer<QWindow> transientParent;
QPointer<QScreen> topLevelScreen;
#ifndef QT_NO_CURSOR
- QCursor cursor;
- bool hasCursor;
+ QCursor cursor = {Qt::ArrowCursor};
+ bool hasCursor = false;
#endif
- bool compositing;
+ bool compositing = false;
QElapsedTimer lastComposeTime;
#if QT_CONFIG(vulkan)
- QVulkanInstance *vulkanInstance;
+ QVulkanInstance *vulkanInstance = nullptr;
#endif
};
diff --git a/src/gui/kernel/qwindowsysteminterface.cpp b/src/gui/kernel/qwindowsysteminterface.cpp
index 759671fbd7..6f3edb10b4 100644
--- a/src/gui/kernel/qwindowsysteminterface.cpp
+++ b/src/gui/kernel/qwindowsysteminterface.cpp
@@ -924,7 +924,11 @@ QPlatformDropQtResponse QWindowSystemInterface::handleDrop(QWindow *window, cons
\note This function can only be called from the GUI thread.
*/
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+bool QWindowSystemInterface::handleNativeEvent(QWindow *window, const QByteArray &eventType, void *message, qintptr *result)
+#else
bool QWindowSystemInterface::handleNativeEvent(QWindow *window, const QByteArray &eventType, void *message, long *result)
+#endif
{
return QGuiApplicationPrivate::processNativeEvent(window, eventType, message, result);
}
diff --git a/src/gui/kernel/qwindowsysteminterface.h b/src/gui/kernel/qwindowsysteminterface.h
index bf98c33a1a..fd70eda9ff 100644
--- a/src/gui/kernel/qwindowsysteminterface.h
+++ b/src/gui/kernel/qwindowsysteminterface.h
@@ -230,7 +230,11 @@ public:
Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers);
#endif // QT_CONFIG(draganddrop)
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+ static bool handleNativeEvent(QWindow *window, const QByteArray &eventType, void *message, qintptr *result);
+#else
static bool handleNativeEvent(QWindow *window, const QByteArray &eventType, void *message, long *result);
+#endif
// Changes to the screen
static void handleScreenAdded(QPlatformScreen *screen, bool isPrimary = false);
diff --git a/src/gui/kernel/qwindowsysteminterface_p.h b/src/gui/kernel/qwindowsysteminterface_p.h
index 6c818a9030..d6513f1836 100644
--- a/src/gui/kernel/qwindowsysteminterface_p.h
+++ b/src/gui/kernel/qwindowsysteminterface_p.h
@@ -123,7 +123,7 @@ public:
class CloseEvent : public WindowSystemEvent {
public:
- explicit CloseEvent(QWindow *w, bool *a = 0)
+ explicit CloseEvent(QWindow *w, bool *a = nullptr)
: WindowSystemEvent(Close), window(w), accepted(a)
{ }
QPointer<QWindow> window;
@@ -398,7 +398,7 @@ public:
class TabletEnterProximityEvent : public InputEvent {
public:
TabletEnterProximityEvent(ulong time, int device, int pointerType, qint64 uid)
- : InputEvent(0, time, TabletEnterProximity, Qt::NoModifier),
+ : InputEvent(nullptr, time, TabletEnterProximity, Qt::NoModifier),
device(device), pointerType(pointerType), uid(uid) { }
int device;
int pointerType;
@@ -408,7 +408,7 @@ public:
class TabletLeaveProximityEvent : public InputEvent {
public:
TabletLeaveProximityEvent(ulong time, int device, int pointerType, qint64 uid)
- : InputEvent(0, time, TabletLeaveProximity, Qt::NoModifier),
+ : InputEvent(nullptr, time, TabletLeaveProximity, Qt::NoModifier),
device(device), pointerType(pointerType), uid(uid) { }
int device;
int pointerType;
@@ -474,7 +474,7 @@ public:
for (int i = 0; i < impl.size(); ++i)
if (!(impl.at(i)->type & QWindowSystemInterfacePrivate::UserInputEvent))
return impl.takeAt(i);
- return 0;
+ return nullptr;
}
bool nonUserInputEventsQueued()
{
@@ -495,7 +495,7 @@ public:
if (impl.at(i)->type == t)
return impl.at(i);
}
- return 0;
+ return nullptr;
}
void remove(const WindowSystemEvent *e)
{
diff --git a/src/gui/math3d/qgenericmatrix.h b/src/gui/math3d/qgenericmatrix.h
index 6a2a9e5bae..692c29c996 100644
--- a/src/gui/math3d/qgenericmatrix.h
+++ b/src/gui/math3d/qgenericmatrix.h
@@ -351,11 +351,11 @@ QDebug operator<<(QDebug dbg, const QGenericMatrix<N, M, T> &m)
QDebugStateSaver saver(dbg);
dbg.nospace() << "QGenericMatrix<" << N << ", " << M
<< ", " << QTypeInfo<T>::name()
- << ">(" << endl << qSetFieldWidth(10);
+ << ">(" << Qt::endl << qSetFieldWidth(10);
for (int row = 0; row < M; ++row) {
for (int col = 0; col < N; ++col)
dbg << m(row, col);
- dbg << endl;
+ dbg << Qt::endl;
}
dbg << qSetFieldWidth(0) << ')';
return dbg;
diff --git a/src/gui/math3d/qmatrix4x4.cpp b/src/gui/math3d/qmatrix4x4.cpp
index 045fa210c4..ad4cdfdbf4 100644
--- a/src/gui/math3d/qmatrix4x4.cpp
+++ b/src/gui/math3d/qmatrix4x4.cpp
@@ -2037,12 +2037,12 @@ QDebug operator<<(QDebug dbg, const QMatrix4x4 &m)
}
// Output in row-major order because it is more human-readable.
- dbg.nospace() << "QMatrix4x4(type:" << bits.constData() << endl
+ dbg.nospace() << "QMatrix4x4(type:" << bits.constData() << Qt::endl
<< qSetFieldWidth(10)
- << m(0, 0) << m(0, 1) << m(0, 2) << m(0, 3) << endl
- << m(1, 0) << m(1, 1) << m(1, 2) << m(1, 3) << endl
- << m(2, 0) << m(2, 1) << m(2, 2) << m(2, 3) << endl
- << m(3, 0) << m(3, 1) << m(3, 2) << m(3, 3) << endl
+ << m(0, 0) << m(0, 1) << m(0, 2) << m(0, 3) << Qt::endl
+ << m(1, 0) << m(1, 1) << m(1, 2) << m(1, 3) << Qt::endl
+ << m(2, 0) << m(2, 1) << m(2, 2) << m(2, 3) << Qt::endl
+ << m(3, 0) << m(3, 1) << m(3, 2) << m(3, 3) << Qt::endl
<< qSetFieldWidth(0) << ')';
return dbg;
}
diff --git a/src/gui/opengl/qopengl.cpp b/src/gui/opengl/qopengl.cpp
index 2b1e57a4bb..667d16317f 100644
--- a/src/gui/opengl/qopengl.cpp
+++ b/src/gui/opengl/qopengl.cpp
@@ -80,7 +80,7 @@ QOpenGLExtensionMatcher::QOpenGLExtensionMatcher()
if (extensionStr) {
QByteArray ba(extensionStr);
QList<QByteArray> extensions = ba.split(' ');
- m_extensions = extensions.toSet();
+ m_extensions = QSet<QByteArray>(extensions.constBegin(), extensions.constEnd());
} else {
#ifdef QT_OPENGL_3
// clear error state
@@ -136,7 +136,7 @@ QDebug operator<<(QDebug d, const QOpenGLConfig::Gpu &g)
d.nospace();
d << "Gpu(";
if (g.isValid()) {
- d << "vendor=" << hex << showbase <<g.vendorId << ", device=" << g.deviceId
+ d << "vendor=" << Qt::hex << Qt::showbase <<g.vendorId << ", device=" << g.deviceId
<< "version=" << g.driverVersion;
} else {
d << 0;
diff --git a/src/gui/opengl/qopengldebug.h b/src/gui/opengl/qopengldebug.h
index 556ec175e1..7363985d60 100644
--- a/src/gui/opengl/qopengldebug.h
+++ b/src/gui/opengl/qopengldebug.h
@@ -110,12 +110,10 @@ public:
QOpenGLDebugMessage(const QOpenGLDebugMessage &debugMessage);
QOpenGLDebugMessage &operator=(const QOpenGLDebugMessage &debugMessage);
-#ifdef Q_COMPILER_RVALUE_REFS
- QOpenGLDebugMessage &operator=(QOpenGLDebugMessage &&other) Q_DECL_NOTHROW { swap(other); return *this; }
-#endif
+ QOpenGLDebugMessage &operator=(QOpenGLDebugMessage &&other) noexcept { swap(other); return *this; }
~QOpenGLDebugMessage();
- void swap(QOpenGLDebugMessage &other) Q_DECL_NOTHROW { qSwap(d, other.d); }
+ void swap(QOpenGLDebugMessage &other) noexcept { qSwap(d, other.d); }
Source source() const;
Type type() const;
diff --git a/src/gui/opengl/qopenglengineshadermanager_p.h b/src/gui/opengl/qopenglengineshadermanager_p.h
index d43788d777..14c79f5de3 100644
--- a/src/gui/opengl/qopenglengineshadermanager_p.h
+++ b/src/gui/opengl/qopenglengineshadermanager_p.h
@@ -370,7 +370,7 @@ private:
class QOpenGLEngineShaderProg
{
public:
- QOpenGLEngineShaderProg() : program(0) {}
+ QOpenGLEngineShaderProg() : program(nullptr) {}
~QOpenGLEngineShaderProg() {
if (program)
diff --git a/src/gui/opengl/qopengles2ext.h b/src/gui/opengl/qopengles2ext.h
index 8ff50a924e..8517e24267 100644
--- a/src/gui/opengl/qopengles2ext.h
+++ b/src/gui/opengl/qopengles2ext.h
@@ -1,5 +1,5 @@
-#ifndef __gl2ext_h_
-#define __gl2ext_h_ 1
+#ifndef __gles2_gl2ext_h_
+#define __gles2_gl2ext_h_ 1
#if 0
#pragma qt_no_master_include
@@ -25,7 +25,7 @@ typedef struct __GLsync *GLsync;
#endif
/*
-** Copyright (c) 2013-2017 The Khronos Group Inc.
+** Copyright (c) 2013-2018 The Khronos Group Inc.
**
** Permission is hereby granted, free of charge, to any person obtaining a
** copy of this software and/or associated documentation files (the
@@ -57,7 +57,7 @@ typedef struct __GLsync *GLsync;
#define GL_APIENTRYP GL_APIENTRY*
#endif
-/* Generated on date 20170331 */
+/* Generated on date 20190228 */
/* Generated C header for:
* API: gles2
@@ -178,6 +178,16 @@ GL_APICALL void GL_APIENTRY glGetPointervKHR (GLenum pname, void **params);
#define GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR 0x00000008
#endif /* GL_KHR_no_error */
+#ifndef GL_KHR_parallel_shader_compile
+#define GL_KHR_parallel_shader_compile 1
+#define GL_MAX_SHADER_COMPILER_THREADS_KHR 0x91B0
+#define GL_COMPLETION_STATUS_KHR 0x91B1
+typedef void (GL_APIENTRYP PFNGLMAXSHADERCOMPILERTHREADSKHRPROC) (GLuint count);
+#ifdef GL_GLEXT_PROTOTYPES
+GL_APICALL void GL_APIENTRY glMaxShaderCompilerThreadsKHR (GLuint count);
+#endif
+#endif /* GL_KHR_parallel_shader_compile */
+
#ifndef GL_KHR_robust_buffer_access_behavior
#define GL_KHR_robust_buffer_access_behavior 1
#endif /* GL_KHR_robust_buffer_access_behavior */
@@ -343,12 +353,12 @@ GL_APICALL GLboolean GL_APIENTRY glIsEnablediOES (GLenum target, GLuint index);
typedef void (GL_APIENTRYP PFNGLDRAWELEMENTSBASEVERTEXOESPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex);
typedef void (GL_APIENTRYP PFNGLDRAWRANGEELEMENTSBASEVERTEXOESPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex);
typedef void (GL_APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXOESPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex);
-typedef void (GL_APIENTRYP PFNGLMULTIDRAWELEMENTSBASEVERTEXOESPROC) (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount, const GLint *basevertex);
+typedef void (GL_APIENTRYP PFNGLMULTIDRAWELEMENTSBASEVERTEXEXTPROC) (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount, const GLint *basevertex);
#ifdef GL_GLEXT_PROTOTYPES
GL_APICALL void GL_APIENTRY glDrawElementsBaseVertexOES (GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex);
GL_APICALL void GL_APIENTRY glDrawRangeElementsBaseVertexOES (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex);
GL_APICALL void GL_APIENTRY glDrawElementsInstancedBaseVertexOES (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex);
-GL_APICALL void GL_APIENTRY glMultiDrawElementsBaseVertexOES (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount, const GLint *basevertex);
+GL_APICALL void GL_APIENTRY glMultiDrawElementsBaseVertexEXT (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount, const GLint *basevertex);
#endif
#endif /* GL_OES_draw_elements_base_vertex */
@@ -810,6 +820,22 @@ GL_APICALL void GL_APIENTRY glGetFloati_vOES (GLenum target, GLuint index, GLflo
#define GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD 0x87EE
#endif /* GL_AMD_compressed_ATC_texture */
+#ifndef GL_AMD_framebuffer_multisample_advanced
+#define GL_AMD_framebuffer_multisample_advanced 1
+#define GL_RENDERBUFFER_STORAGE_SAMPLES_AMD 0x91B2
+#define GL_MAX_COLOR_FRAMEBUFFER_SAMPLES_AMD 0x91B3
+#define GL_MAX_COLOR_FRAMEBUFFER_STORAGE_SAMPLES_AMD 0x91B4
+#define GL_MAX_DEPTH_STENCIL_FRAMEBUFFER_SAMPLES_AMD 0x91B5
+#define GL_NUM_SUPPORTED_MULTISAMPLE_MODES_AMD 0x91B6
+#define GL_SUPPORTED_MULTISAMPLE_MODES_AMD 0x91B7
+typedef void (GL_APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLEADVANCEDAMDPROC) (GLenum target, GLsizei samples, GLsizei storageSamples, GLenum internalformat, GLsizei width, GLsizei height);
+typedef void (GL_APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEADVANCEDAMDPROC) (GLuint renderbuffer, GLsizei samples, GLsizei storageSamples, GLenum internalformat, GLsizei width, GLsizei height);
+#ifdef GL_GLEXT_PROTOTYPES
+GL_APICALL void GL_APIENTRY glRenderbufferStorageMultisampleAdvancedAMD (GLenum target, GLsizei samples, GLsizei storageSamples, GLenum internalformat, GLsizei width, GLsizei height);
+GL_APICALL void GL_APIENTRY glNamedRenderbufferStorageMultisampleAdvancedAMD (GLuint renderbuffer, GLsizei samples, GLsizei storageSamples, GLenum internalformat, GLsizei width, GLsizei height);
+#endif
+#endif /* GL_AMD_framebuffer_multisample_advanced */
+
#ifndef GL_AMD_performance_monitor
#define GL_AMD_performance_monitor 1
#define GL_COUNTER_TYPE_AMD 0x8BC0
@@ -1070,6 +1096,20 @@ GL_APICALL void GL_APIENTRY glGetSyncivAPPLE (GLsync sync, GLenum pname, GLsizei
#define GL_SHADER_BINARY_DMP 0x9250
#endif /* GL_DMP_shader_binary */
+#ifndef GL_EXT_EGL_image_array
+#define GL_EXT_EGL_image_array 1
+#endif /* GL_EXT_EGL_image_array */
+
+#ifndef GL_EXT_EGL_image_storage
+#define GL_EXT_EGL_image_storage 1
+typedef void (GL_APIENTRYP PFNGLEGLIMAGETARGETTEXSTORAGEEXTPROC) (GLenum target, GLeglImageOES image, const GLint* attrib_list);
+typedef void (GL_APIENTRYP PFNGLEGLIMAGETARGETTEXTURESTORAGEEXTPROC) (GLuint texture, GLeglImageOES image, const GLint* attrib_list);
+#ifdef GL_GLEXT_PROTOTYPES
+GL_APICALL void GL_APIENTRY glEGLImageTargetTexStorageEXT (GLenum target, GLeglImageOES image, const GLint* attrib_list);
+GL_APICALL void GL_APIENTRY glEGLImageTargetTextureStorageEXT (GLuint texture, GLeglImageOES image, const GLint* attrib_list);
+#endif
+#endif /* GL_EXT_EGL_image_storage */
+
#ifndef GL_EXT_YUV_target
#define GL_EXT_YUV_target 1
#define GL_SAMPLER_EXTERNAL_2D_Y2Y_EXT 0x8BE7
@@ -1141,6 +1181,20 @@ GL_APICALL void GL_APIENTRY glClearTexSubImageEXT (GLuint texture, GLint level,
#endif
#endif /* GL_EXT_clear_texture */
+#ifndef GL_EXT_clip_control
+#define GL_EXT_clip_control 1
+#define GL_LOWER_LEFT_EXT 0x8CA1
+#define GL_UPPER_LEFT_EXT 0x8CA2
+#define GL_NEGATIVE_ONE_TO_ONE_EXT 0x935E
+#define GL_ZERO_TO_ONE_EXT 0x935F
+#define GL_CLIP_ORIGIN_EXT 0x935C
+#define GL_CLIP_DEPTH_MODE_EXT 0x935D
+typedef void (GL_APIENTRYP PFNGLCLIPCONTROLEXTPROC) (GLenum origin, GLenum depth);
+#ifdef GL_GLEXT_PROTOTYPES
+GL_APICALL void GL_APIENTRY glClipControlEXT (GLenum origin, GLenum depth);
+#endif
+#endif /* GL_EXT_clip_control */
+
#ifndef GL_EXT_clip_cull_distance
#define GL_EXT_clip_cull_distance 1
#define GL_MAX_CLIP_DISTANCES_EXT 0x0D32
@@ -1211,6 +1265,11 @@ GL_APICALL void GL_APIENTRY glPopGroupMarkerEXT (void);
#endif
#endif /* GL_EXT_debug_marker */
+#ifndef GL_EXT_depth_clamp
+#define GL_EXT_depth_clamp 1
+#define GL_DEPTH_CLAMP_EXT 0x864F
+#endif /* GL_EXT_depth_clamp */
+
#ifndef GL_EXT_discard_framebuffer
#define GL_EXT_discard_framebuffer 1
#define GL_COLOR_EXT 0x1800
@@ -1326,12 +1385,10 @@ GL_APICALL GLboolean GL_APIENTRY glIsEnablediEXT (GLenum target, GLuint index);
typedef void (GL_APIENTRYP PFNGLDRAWELEMENTSBASEVERTEXEXTPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex);
typedef void (GL_APIENTRYP PFNGLDRAWRANGEELEMENTSBASEVERTEXEXTPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex);
typedef void (GL_APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXEXTPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex);
-typedef void (GL_APIENTRYP PFNGLMULTIDRAWELEMENTSBASEVERTEXEXTPROC) (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount, const GLint *basevertex);
#ifdef GL_GLEXT_PROTOTYPES
GL_APICALL void GL_APIENTRY glDrawElementsBaseVertexEXT (GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex);
GL_APICALL void GL_APIENTRY glDrawRangeElementsBaseVertexEXT (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex);
GL_APICALL void GL_APIENTRY glDrawElementsInstancedBaseVertexEXT (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex);
-GL_APICALL void GL_APIENTRY glMultiDrawElementsBaseVertexEXT (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount, const GLint *basevertex);
#endif
#endif /* GL_EXT_draw_elements_base_vertex */
@@ -1355,6 +1412,17 @@ GL_APICALL void GL_APIENTRY glDrawTransformFeedbackInstancedEXT (GLenum mode, GL
#endif
#endif /* GL_EXT_draw_transform_feedback */
+#ifndef GL_EXT_external_buffer
+#define GL_EXT_external_buffer 1
+typedef void *GLeglClientBufferEXT;
+typedef void (GL_APIENTRYP PFNGLBUFFERSTORAGEEXTERNALEXTPROC) (GLenum target, GLintptr offset, GLsizeiptr size, GLeglClientBufferEXT clientBuffer, GLbitfield flags);
+typedef void (GL_APIENTRYP PFNGLNAMEDBUFFERSTORAGEEXTERNALEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, GLeglClientBufferEXT clientBuffer, GLbitfield flags);
+#ifdef GL_GLEXT_PROTOTYPES
+GL_APICALL void GL_APIENTRY glBufferStorageExternalEXT (GLenum target, GLintptr offset, GLsizeiptr size, GLeglClientBufferEXT clientBuffer, GLbitfield flags);
+GL_APICALL void GL_APIENTRY glNamedBufferStorageExternalEXT (GLuint buffer, GLintptr offset, GLsizeiptr size, GLeglClientBufferEXT clientBuffer, GLbitfield flags);
+#endif
+#endif /* GL_EXT_external_buffer */
+
#ifndef GL_EXT_float_blend
#define GL_EXT_float_blend 1
#endif /* GL_EXT_float_blend */
@@ -1433,6 +1501,85 @@ GL_APICALL void GL_APIENTRY glFlushMappedBufferRangeEXT (GLenum target, GLintptr
#endif
#endif /* GL_EXT_map_buffer_range */
+#ifndef GL_EXT_memory_object
+#define GL_EXT_memory_object 1
+#define GL_TEXTURE_TILING_EXT 0x9580
+#define GL_DEDICATED_MEMORY_OBJECT_EXT 0x9581
+#define GL_PROTECTED_MEMORY_OBJECT_EXT 0x959B
+#define GL_NUM_TILING_TYPES_EXT 0x9582
+#define GL_TILING_TYPES_EXT 0x9583
+#define GL_OPTIMAL_TILING_EXT 0x9584
+#define GL_LINEAR_TILING_EXT 0x9585
+#define GL_NUM_DEVICE_UUIDS_EXT 0x9596
+#define GL_DEVICE_UUID_EXT 0x9597
+#define GL_DRIVER_UUID_EXT 0x9598
+#define GL_UUID_SIZE_EXT 16
+typedef void (GL_APIENTRYP PFNGLGETUNSIGNEDBYTEVEXTPROC) (GLenum pname, GLubyte *data);
+typedef void (GL_APIENTRYP PFNGLGETUNSIGNEDBYTEI_VEXTPROC) (GLenum target, GLuint index, GLubyte *data);
+typedef void (GL_APIENTRYP PFNGLDELETEMEMORYOBJECTSEXTPROC) (GLsizei n, const GLuint *memoryObjects);
+typedef GLboolean (GL_APIENTRYP PFNGLISMEMORYOBJECTEXTPROC) (GLuint memoryObject);
+typedef void (GL_APIENTRYP PFNGLCREATEMEMORYOBJECTSEXTPROC) (GLsizei n, GLuint *memoryObjects);
+typedef void (GL_APIENTRYP PFNGLMEMORYOBJECTPARAMETERIVEXTPROC) (GLuint memoryObject, GLenum pname, const GLint *params);
+typedef void (GL_APIENTRYP PFNGLGETMEMORYOBJECTPARAMETERIVEXTPROC) (GLuint memoryObject, GLenum pname, GLint *params);
+typedef void (GL_APIENTRYP PFNGLTEXSTORAGEMEM2DEXTPROC) (GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLuint memory, GLuint64 offset);
+typedef void (GL_APIENTRYP PFNGLTEXSTORAGEMEM2DMULTISAMPLEEXTPROC) (GLenum target, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations, GLuint memory, GLuint64 offset);
+typedef void (GL_APIENTRYP PFNGLTEXSTORAGEMEM3DEXTPROC) (GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLuint memory, GLuint64 offset);
+typedef void (GL_APIENTRYP PFNGLTEXSTORAGEMEM3DMULTISAMPLEEXTPROC) (GLenum target, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations, GLuint memory, GLuint64 offset);
+typedef void (GL_APIENTRYP PFNGLBUFFERSTORAGEMEMEXTPROC) (GLenum target, GLsizeiptr size, GLuint memory, GLuint64 offset);
+typedef void (GL_APIENTRYP PFNGLTEXTURESTORAGEMEM2DEXTPROC) (GLuint texture, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLuint memory, GLuint64 offset);
+typedef void (GL_APIENTRYP PFNGLTEXTURESTORAGEMEM2DMULTISAMPLEEXTPROC) (GLuint texture, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations, GLuint memory, GLuint64 offset);
+typedef void (GL_APIENTRYP PFNGLTEXTURESTORAGEMEM3DEXTPROC) (GLuint texture, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLuint memory, GLuint64 offset);
+typedef void (GL_APIENTRYP PFNGLTEXTURESTORAGEMEM3DMULTISAMPLEEXTPROC) (GLuint texture, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations, GLuint memory, GLuint64 offset);
+typedef void (GL_APIENTRYP PFNGLNAMEDBUFFERSTORAGEMEMEXTPROC) (GLuint buffer, GLsizeiptr size, GLuint memory, GLuint64 offset);
+#ifdef GL_GLEXT_PROTOTYPES
+GL_APICALL void GL_APIENTRY glGetUnsignedBytevEXT (GLenum pname, GLubyte *data);
+GL_APICALL void GL_APIENTRY glGetUnsignedBytei_vEXT (GLenum target, GLuint index, GLubyte *data);
+GL_APICALL void GL_APIENTRY glDeleteMemoryObjectsEXT (GLsizei n, const GLuint *memoryObjects);
+GL_APICALL GLboolean GL_APIENTRY glIsMemoryObjectEXT (GLuint memoryObject);
+GL_APICALL void GL_APIENTRY glCreateMemoryObjectsEXT (GLsizei n, GLuint *memoryObjects);
+GL_APICALL void GL_APIENTRY glMemoryObjectParameterivEXT (GLuint memoryObject, GLenum pname, const GLint *params);
+GL_APICALL void GL_APIENTRY glGetMemoryObjectParameterivEXT (GLuint memoryObject, GLenum pname, GLint *params);
+GL_APICALL void GL_APIENTRY glTexStorageMem2DEXT (GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLuint memory, GLuint64 offset);
+GL_APICALL void GL_APIENTRY glTexStorageMem2DMultisampleEXT (GLenum target, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations, GLuint memory, GLuint64 offset);
+GL_APICALL void GL_APIENTRY glTexStorageMem3DEXT (GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLuint memory, GLuint64 offset);
+GL_APICALL void GL_APIENTRY glTexStorageMem3DMultisampleEXT (GLenum target, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations, GLuint memory, GLuint64 offset);
+GL_APICALL void GL_APIENTRY glBufferStorageMemEXT (GLenum target, GLsizeiptr size, GLuint memory, GLuint64 offset);
+GL_APICALL void GL_APIENTRY glTextureStorageMem2DEXT (GLuint texture, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLuint memory, GLuint64 offset);
+GL_APICALL void GL_APIENTRY glTextureStorageMem2DMultisampleEXT (GLuint texture, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations, GLuint memory, GLuint64 offset);
+GL_APICALL void GL_APIENTRY glTextureStorageMem3DEXT (GLuint texture, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLuint memory, GLuint64 offset);
+GL_APICALL void GL_APIENTRY glTextureStorageMem3DMultisampleEXT (GLuint texture, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations, GLuint memory, GLuint64 offset);
+GL_APICALL void GL_APIENTRY glNamedBufferStorageMemEXT (GLuint buffer, GLsizeiptr size, GLuint memory, GLuint64 offset);
+#endif
+#endif /* GL_EXT_memory_object */
+
+#ifndef GL_EXT_memory_object_fd
+#define GL_EXT_memory_object_fd 1
+#define GL_HANDLE_TYPE_OPAQUE_FD_EXT 0x9586
+typedef void (GL_APIENTRYP PFNGLIMPORTMEMORYFDEXTPROC) (GLuint memory, GLuint64 size, GLenum handleType, GLint fd);
+#ifdef GL_GLEXT_PROTOTYPES
+GL_APICALL void GL_APIENTRY glImportMemoryFdEXT (GLuint memory, GLuint64 size, GLenum handleType, GLint fd);
+#endif
+#endif /* GL_EXT_memory_object_fd */
+
+#ifndef GL_EXT_memory_object_win32
+#define GL_EXT_memory_object_win32 1
+#define GL_HANDLE_TYPE_OPAQUE_WIN32_EXT 0x9587
+#define GL_HANDLE_TYPE_OPAQUE_WIN32_KMT_EXT 0x9588
+#define GL_DEVICE_LUID_EXT 0x9599
+#define GL_DEVICE_NODE_MASK_EXT 0x959A
+#define GL_LUID_SIZE_EXT 8
+#define GL_HANDLE_TYPE_D3D12_TILEPOOL_EXT 0x9589
+#define GL_HANDLE_TYPE_D3D12_RESOURCE_EXT 0x958A
+#define GL_HANDLE_TYPE_D3D11_IMAGE_EXT 0x958B
+#define GL_HANDLE_TYPE_D3D11_IMAGE_KMT_EXT 0x958C
+typedef void (GL_APIENTRYP PFNGLIMPORTMEMORYWIN32HANDLEEXTPROC) (GLuint memory, GLuint64 size, GLenum handleType, void *handle);
+typedef void (GL_APIENTRYP PFNGLIMPORTMEMORYWIN32NAMEEXTPROC) (GLuint memory, GLuint64 size, GLenum handleType, const void *name);
+#ifdef GL_GLEXT_PROTOTYPES
+GL_APICALL void GL_APIENTRY glImportMemoryWin32HandleEXT (GLuint memory, GLuint64 size, GLenum handleType, void *handle);
+GL_APICALL void GL_APIENTRY glImportMemoryWin32NameEXT (GLuint memory, GLuint64 size, GLenum handleType, const void *name);
+#endif
+#endif /* GL_EXT_memory_object_win32 */
+
#ifndef GL_EXT_multi_draw_arrays
#define GL_EXT_multi_draw_arrays 1
typedef void (GL_APIENTRYP PFNGLMULTIDRAWARRAYSEXTPROC) (GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount);
@@ -1598,6 +1745,55 @@ GL_APICALL void GL_APIENTRY glGetnUniformivEXT (GLuint program, GLint location,
#define GL_FRAMEBUFFER_SRGB_EXT 0x8DB9
#endif /* GL_EXT_sRGB_write_control */
+#ifndef GL_EXT_semaphore
+#define GL_EXT_semaphore 1
+#define GL_LAYOUT_GENERAL_EXT 0x958D
+#define GL_LAYOUT_COLOR_ATTACHMENT_EXT 0x958E
+#define GL_LAYOUT_DEPTH_STENCIL_ATTACHMENT_EXT 0x958F
+#define GL_LAYOUT_DEPTH_STENCIL_READ_ONLY_EXT 0x9590
+#define GL_LAYOUT_SHADER_READ_ONLY_EXT 0x9591
+#define GL_LAYOUT_TRANSFER_SRC_EXT 0x9592
+#define GL_LAYOUT_TRANSFER_DST_EXT 0x9593
+#define GL_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_EXT 0x9530
+#define GL_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_EXT 0x9531
+typedef void (GL_APIENTRYP PFNGLGENSEMAPHORESEXTPROC) (GLsizei n, GLuint *semaphores);
+typedef void (GL_APIENTRYP PFNGLDELETESEMAPHORESEXTPROC) (GLsizei n, const GLuint *semaphores);
+typedef GLboolean (GL_APIENTRYP PFNGLISSEMAPHOREEXTPROC) (GLuint semaphore);
+typedef void (GL_APIENTRYP PFNGLSEMAPHOREPARAMETERUI64VEXTPROC) (GLuint semaphore, GLenum pname, const GLuint64 *params);
+typedef void (GL_APIENTRYP PFNGLGETSEMAPHOREPARAMETERUI64VEXTPROC) (GLuint semaphore, GLenum pname, GLuint64 *params);
+typedef void (GL_APIENTRYP PFNGLWAITSEMAPHOREEXTPROC) (GLuint semaphore, GLuint numBufferBarriers, const GLuint *buffers, GLuint numTextureBarriers, const GLuint *textures, const GLenum *srcLayouts);
+typedef void (GL_APIENTRYP PFNGLSIGNALSEMAPHOREEXTPROC) (GLuint semaphore, GLuint numBufferBarriers, const GLuint *buffers, GLuint numTextureBarriers, const GLuint *textures, const GLenum *dstLayouts);
+#ifdef GL_GLEXT_PROTOTYPES
+GL_APICALL void GL_APIENTRY glGenSemaphoresEXT (GLsizei n, GLuint *semaphores);
+GL_APICALL void GL_APIENTRY glDeleteSemaphoresEXT (GLsizei n, const GLuint *semaphores);
+GL_APICALL GLboolean GL_APIENTRY glIsSemaphoreEXT (GLuint semaphore);
+GL_APICALL void GL_APIENTRY glSemaphoreParameterui64vEXT (GLuint semaphore, GLenum pname, const GLuint64 *params);
+GL_APICALL void GL_APIENTRY glGetSemaphoreParameterui64vEXT (GLuint semaphore, GLenum pname, GLuint64 *params);
+GL_APICALL void GL_APIENTRY glWaitSemaphoreEXT (GLuint semaphore, GLuint numBufferBarriers, const GLuint *buffers, GLuint numTextureBarriers, const GLuint *textures, const GLenum *srcLayouts);
+GL_APICALL void GL_APIENTRY glSignalSemaphoreEXT (GLuint semaphore, GLuint numBufferBarriers, const GLuint *buffers, GLuint numTextureBarriers, const GLuint *textures, const GLenum *dstLayouts);
+#endif
+#endif /* GL_EXT_semaphore */
+
+#ifndef GL_EXT_semaphore_fd
+#define GL_EXT_semaphore_fd 1
+typedef void (GL_APIENTRYP PFNGLIMPORTSEMAPHOREFDEXTPROC) (GLuint semaphore, GLenum handleType, GLint fd);
+#ifdef GL_GLEXT_PROTOTYPES
+GL_APICALL void GL_APIENTRY glImportSemaphoreFdEXT (GLuint semaphore, GLenum handleType, GLint fd);
+#endif
+#endif /* GL_EXT_semaphore_fd */
+
+#ifndef GL_EXT_semaphore_win32
+#define GL_EXT_semaphore_win32 1
+#define GL_HANDLE_TYPE_D3D12_FENCE_EXT 0x9594
+#define GL_D3D12_FENCE_VALUE_EXT 0x9595
+typedef void (GL_APIENTRYP PFNGLIMPORTSEMAPHOREWIN32HANDLEEXTPROC) (GLuint semaphore, GLenum handleType, void *handle);
+typedef void (GL_APIENTRYP PFNGLIMPORTSEMAPHOREWIN32NAMEEXTPROC) (GLuint semaphore, GLenum handleType, const void *name);
+#ifdef GL_GLEXT_PROTOTYPES
+GL_APICALL void GL_APIENTRY glImportSemaphoreWin32HandleEXT (GLuint semaphore, GLenum handleType, void *handle);
+GL_APICALL void GL_APIENTRY glImportSemaphoreWin32NameEXT (GLuint semaphore, GLenum handleType, const void *name);
+#endif
+#endif /* GL_EXT_semaphore_win32 */
+
#ifndef GL_EXT_separate_shader_objects
#define GL_EXT_separate_shader_objects 1
#define GL_ACTIVE_PROGRAM_EXT 0x8259
@@ -1703,6 +1899,14 @@ GL_APICALL void GL_APIENTRY glProgramUniformMatrix4x3fvEXT (GLuint program, GLin
#define GL_FRAGMENT_SHADER_DISCARDS_SAMPLES_EXT 0x8A52
#endif /* GL_EXT_shader_framebuffer_fetch */
+#ifndef GL_EXT_shader_framebuffer_fetch_non_coherent
+#define GL_EXT_shader_framebuffer_fetch_non_coherent 1
+typedef void (GL_APIENTRYP PFNGLFRAMEBUFFERFETCHBARRIEREXTPROC) (void);
+#ifdef GL_GLEXT_PROTOTYPES
+GL_APICALL void GL_APIENTRY glFramebufferFetchBarrierEXT (void);
+#endif
+#endif /* GL_EXT_shader_framebuffer_fetch_non_coherent */
+
#ifndef GL_EXT_shader_group_vote
#define GL_EXT_shader_group_vote 1
#endif /* GL_EXT_shader_group_vote */
@@ -1890,18 +2094,42 @@ GL_APICALL void GL_APIENTRY glTexBufferRangeEXT (GLenum target, GLenum internalf
#define GL_TEXTURE_ASTC_DECODE_PRECISION_EXT 0x8F69
#endif /* GL_EXT_texture_compression_astc_decode_mode */
+#ifndef GL_EXT_texture_compression_bptc
+#define GL_EXT_texture_compression_bptc 1
+#define GL_COMPRESSED_RGBA_BPTC_UNORM_EXT 0x8E8C
+#define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT 0x8E8D
+#define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_EXT 0x8E8E
+#define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_EXT 0x8E8F
+#endif /* GL_EXT_texture_compression_bptc */
+
#ifndef GL_EXT_texture_compression_dxt1
#define GL_EXT_texture_compression_dxt1 1
#define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0
#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1
#endif /* GL_EXT_texture_compression_dxt1 */
+#ifndef GL_EXT_texture_compression_rgtc
+#define GL_EXT_texture_compression_rgtc 1
+#define GL_COMPRESSED_RED_RGTC1_EXT 0x8DBB
+#define GL_COMPRESSED_SIGNED_RED_RGTC1_EXT 0x8DBC
+#define GL_COMPRESSED_RED_GREEN_RGTC2_EXT 0x8DBD
+#define GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT 0x8DBE
+#endif /* GL_EXT_texture_compression_rgtc */
+
#ifndef GL_EXT_texture_compression_s3tc
#define GL_EXT_texture_compression_s3tc 1
#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2
#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3
#endif /* GL_EXT_texture_compression_s3tc */
+#ifndef GL_EXT_texture_compression_s3tc_srgb
+#define GL_EXT_texture_compression_s3tc_srgb 1
+#define GL_COMPRESSED_SRGB_S3TC_DXT1_EXT 0x8C4C
+#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT 0x8C4D
+#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT 0x8C4E
+#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT 0x8C4F
+#endif /* GL_EXT_texture_compression_s3tc_srgb */
+
#ifndef GL_EXT_texture_cube_map_array
#define GL_EXT_texture_cube_map_array 1
#define GL_TEXTURE_CUBE_MAP_ARRAY_EXT 0x9009
@@ -1923,12 +2151,24 @@ GL_APICALL void GL_APIENTRY glTexBufferRangeEXT (GLenum target, GLenum internalf
#ifndef GL_EXT_texture_filter_minmax
#define GL_EXT_texture_filter_minmax 1
+#define GL_TEXTURE_REDUCTION_MODE_EXT 0x9366
+#define GL_WEIGHTED_AVERAGE_EXT 0x9367
#endif /* GL_EXT_texture_filter_minmax */
#ifndef GL_EXT_texture_format_BGRA8888
#define GL_EXT_texture_format_BGRA8888 1
#endif /* GL_EXT_texture_format_BGRA8888 */
+#ifndef GL_EXT_texture_format_sRGB_override
+#define GL_EXT_texture_format_sRGB_override 1
+#define GL_TEXTURE_FORMAT_SRGB_OVERRIDE_EXT 0x8FBF
+#endif /* GL_EXT_texture_format_sRGB_override */
+
+#ifndef GL_EXT_texture_mirror_clamp_to_edge
+#define GL_EXT_texture_mirror_clamp_to_edge 1
+#define GL_MIRROR_CLAMP_TO_EDGE_EXT 0x8743
+#endif /* GL_EXT_texture_mirror_clamp_to_edge */
+
#ifndef GL_EXT_texture_norm16
#define GL_EXT_texture_norm16 1
#define GL_R16_EXT 0x822A
@@ -1938,6 +2178,10 @@ GL_APICALL void GL_APIENTRY glTexBufferRangeEXT (GLenum target, GLenum internalf
#define GL_RGB16_SNORM_EXT 0x8F9A
#endif /* GL_EXT_texture_norm16 */
+#ifndef GL_EXT_texture_query_lod
+#define GL_EXT_texture_query_lod 1
+#endif /* GL_EXT_texture_query_lod */
+
#ifndef GL_EXT_texture_rg
#define GL_EXT_texture_rg 1
#define GL_RED_EXT 0x1903
@@ -2019,6 +2263,16 @@ GL_APICALL void GL_APIENTRY glTextureViewEXT (GLuint texture, GLenum target, GLu
#define GL_UNPACK_SKIP_PIXELS_EXT 0x0CF4
#endif /* GL_EXT_unpack_subimage */
+#ifndef GL_EXT_win32_keyed_mutex
+#define GL_EXT_win32_keyed_mutex 1
+typedef GLboolean (GL_APIENTRYP PFNGLACQUIREKEYEDMUTEXWIN32EXTPROC) (GLuint memory, GLuint64 key, GLuint timeout);
+typedef GLboolean (GL_APIENTRYP PFNGLRELEASEKEYEDMUTEXWIN32EXTPROC) (GLuint memory, GLuint64 key);
+#ifdef GL_GLEXT_PROTOTYPES
+GL_APICALL GLboolean GL_APIENTRY glAcquireKeyedMutexWin32EXT (GLuint memory, GLuint64 key, GLuint timeout);
+GL_APICALL GLboolean GL_APIENTRY glReleaseKeyedMutexWin32EXT (GLuint memory, GLuint64 key);
+#endif
+#endif /* GL_EXT_win32_keyed_mutex */
+
#ifndef GL_EXT_window_rectangles
#define GL_EXT_window_rectangles 1
#define GL_INCLUSIVE_EXT 0x8F10
@@ -2121,6 +2375,11 @@ GL_APICALL void GL_APIENTRY glFramebufferTexture2DMultisampleIMG (GLenum target,
#define GL_CUBIC_MIPMAP_LINEAR_IMG 0x913B
#endif /* GL_IMG_texture_filter_cubic */
+#ifndef GL_INTEL_blackhole_render
+#define GL_INTEL_blackhole_render 1
+#define GL_BLACKHOLE_RENDER_INTEL 0x83FC
+#endif /* GL_INTEL_blackhole_render */
+
#ifndef GL_INTEL_conservative_rasterization
#define GL_INTEL_conservative_rasterization 1
#define GL_CONSERVATIVE_RASTERIZATION_INTEL 0x83FE
@@ -2163,7 +2422,7 @@ typedef void (GL_APIENTRYP PFNGLENDPERFQUERYINTELPROC) (GLuint queryHandle);
typedef void (GL_APIENTRYP PFNGLGETFIRSTPERFQUERYIDINTELPROC) (GLuint *queryId);
typedef void (GL_APIENTRYP PFNGLGETNEXTPERFQUERYIDINTELPROC) (GLuint queryId, GLuint *nextQueryId);
typedef void (GL_APIENTRYP PFNGLGETPERFCOUNTERINFOINTELPROC) (GLuint queryId, GLuint counterId, GLuint counterNameLength, GLchar *counterName, GLuint counterDescLength, GLchar *counterDesc, GLuint *counterOffset, GLuint *counterDataSize, GLuint *counterTypeEnum, GLuint *counterDataTypeEnum, GLuint64 *rawCounterMaxValue);
-typedef void (GL_APIENTRYP PFNGLGETPERFQUERYDATAINTELPROC) (GLuint queryHandle, GLuint flags, GLsizei dataSize, GLvoid *data, GLuint *bytesWritten);
+typedef void (GL_APIENTRYP PFNGLGETPERFQUERYDATAINTELPROC) (GLuint queryHandle, GLuint flags, GLsizei dataSize, void *data, GLuint *bytesWritten);
typedef void (GL_APIENTRYP PFNGLGETPERFQUERYIDBYNAMEINTELPROC) (GLchar *queryName, GLuint *queryId);
typedef void (GL_APIENTRYP PFNGLGETPERFQUERYINFOINTELPROC) (GLuint queryId, GLuint queryNameLength, GLchar *queryName, GLuint *dataSize, GLuint *noCounters, GLuint *noInstances, GLuint *capsMask);
#ifdef GL_GLEXT_PROTOTYPES
@@ -2174,12 +2433,22 @@ GL_APICALL void GL_APIENTRY glEndPerfQueryINTEL (GLuint queryHandle);
GL_APICALL void GL_APIENTRY glGetFirstPerfQueryIdINTEL (GLuint *queryId);
GL_APICALL void GL_APIENTRY glGetNextPerfQueryIdINTEL (GLuint queryId, GLuint *nextQueryId);
GL_APICALL void GL_APIENTRY glGetPerfCounterInfoINTEL (GLuint queryId, GLuint counterId, GLuint counterNameLength, GLchar *counterName, GLuint counterDescLength, GLchar *counterDesc, GLuint *counterOffset, GLuint *counterDataSize, GLuint *counterTypeEnum, GLuint *counterDataTypeEnum, GLuint64 *rawCounterMaxValue);
-GL_APICALL void GL_APIENTRY glGetPerfQueryDataINTEL (GLuint queryHandle, GLuint flags, GLsizei dataSize, GLvoid *data, GLuint *bytesWritten);
+GL_APICALL void GL_APIENTRY glGetPerfQueryDataINTEL (GLuint queryHandle, GLuint flags, GLsizei dataSize, void *data, GLuint *bytesWritten);
GL_APICALL void GL_APIENTRY glGetPerfQueryIdByNameINTEL (GLchar *queryName, GLuint *queryId);
GL_APICALL void GL_APIENTRY glGetPerfQueryInfoINTEL (GLuint queryId, GLuint queryNameLength, GLchar *queryName, GLuint *dataSize, GLuint *noCounters, GLuint *noInstances, GLuint *capsMask);
#endif
#endif /* GL_INTEL_performance_query */
+#ifndef GL_MESA_framebuffer_flip_y
+#define GL_MESA_framebuffer_flip_y 1
+#define GL_FRAMEBUFFER_FLIP_Y_MESA 0x8BBB
+#endif /* GL_MESA_framebuffer_flip_y */
+
+#ifndef GL_MESA_program_binary_formats
+#define GL_MESA_program_binary_formats 1
+#define GL_PROGRAM_BINARY_FORMAT_MESA 0x875F
+#endif /* GL_MESA_program_binary_formats */
+
#ifndef GL_MESA_shader_integer_functions
#define GL_MESA_shader_integer_functions 1
#endif /* GL_MESA_shader_integer_functions */
@@ -2284,6 +2553,27 @@ GL_APICALL void GL_APIENTRY glBlendBarrierNV (void);
#define GL_BLEND_ADVANCED_COHERENT_NV 0x9285
#endif /* GL_NV_blend_equation_advanced_coherent */
+#ifndef GL_NV_blend_minmax_factor
+#define GL_NV_blend_minmax_factor 1
+#define GL_FACTOR_MIN_AMD 0x901C
+#define GL_FACTOR_MAX_AMD 0x901D
+#endif /* GL_NV_blend_minmax_factor */
+
+#ifndef GL_NV_clip_space_w_scaling
+#define GL_NV_clip_space_w_scaling 1
+#define GL_VIEWPORT_POSITION_W_SCALE_NV 0x937C
+#define GL_VIEWPORT_POSITION_W_SCALE_X_COEFF_NV 0x937D
+#define GL_VIEWPORT_POSITION_W_SCALE_Y_COEFF_NV 0x937E
+typedef void (GL_APIENTRYP PFNGLVIEWPORTPOSITIONWSCALENVPROC) (GLuint index, GLfloat xcoeff, GLfloat ycoeff);
+#ifdef GL_GLEXT_PROTOTYPES
+GL_APICALL void GL_APIENTRY glViewportPositionWScaleNV (GLuint index, GLfloat xcoeff, GLfloat ycoeff);
+#endif
+#endif /* GL_NV_clip_space_w_scaling */
+
+#ifndef GL_NV_compute_shader_derivatives
+#define GL_NV_compute_shader_derivatives 1
+#endif /* GL_NV_compute_shader_derivatives */
+
#ifndef GL_NV_conditional_render
#define GL_NV_conditional_render 1
#define GL_QUERY_WAIT_NV 0x8E13
@@ -2310,6 +2600,11 @@ GL_APICALL void GL_APIENTRY glSubpixelPrecisionBiasNV (GLuint xbits, GLuint ybit
#endif
#endif /* GL_NV_conservative_raster */
+#ifndef GL_NV_conservative_raster_pre_snap
+#define GL_NV_conservative_raster_pre_snap 1
+#define GL_CONSERVATIVE_RASTER_MODE_PRE_SNAP_NV 0x9550
+#endif /* GL_NV_conservative_raster_pre_snap */
+
#ifndef GL_NV_conservative_raster_pre_snap_triangles
#define GL_NV_conservative_raster_pre_snap_triangles 1
#define GL_CONSERVATIVE_RASTER_MODE_NV 0x954D
@@ -2655,6 +2950,34 @@ GL_APICALL void GL_APIENTRY glGetInternalformatSampleivNV (GLenum target, GLenum
#endif
#endif /* GL_NV_internalformat_sample_query */
+#ifndef GL_NV_memory_attachment
+#define GL_NV_memory_attachment 1
+#define GL_ATTACHED_MEMORY_OBJECT_NV 0x95A4
+#define GL_ATTACHED_MEMORY_OFFSET_NV 0x95A5
+#define GL_MEMORY_ATTACHABLE_ALIGNMENT_NV 0x95A6
+#define GL_MEMORY_ATTACHABLE_SIZE_NV 0x95A7
+#define GL_MEMORY_ATTACHABLE_NV 0x95A8
+#define GL_DETACHED_MEMORY_INCARNATION_NV 0x95A9
+#define GL_DETACHED_TEXTURES_NV 0x95AA
+#define GL_DETACHED_BUFFERS_NV 0x95AB
+#define GL_MAX_DETACHED_TEXTURES_NV 0x95AC
+#define GL_MAX_DETACHED_BUFFERS_NV 0x95AD
+typedef void (GL_APIENTRYP PFNGLGETMEMORYOBJECTDETACHEDRESOURCESUIVNVPROC) (GLuint memory, GLenum pname, GLint first, GLsizei count, GLuint *params);
+typedef void (GL_APIENTRYP PFNGLRESETMEMORYOBJECTPARAMETERNVPROC) (GLuint memory, GLenum pname);
+typedef void (GL_APIENTRYP PFNGLTEXATTACHMEMORYNVPROC) (GLenum target, GLuint memory, GLuint64 offset);
+typedef void (GL_APIENTRYP PFNGLBUFFERATTACHMEMORYNVPROC) (GLenum target, GLuint memory, GLuint64 offset);
+typedef void (GL_APIENTRYP PFNGLTEXTUREATTACHMEMORYNVPROC) (GLuint texture, GLuint memory, GLuint64 offset);
+typedef void (GL_APIENTRYP PFNGLNAMEDBUFFERATTACHMEMORYNVPROC) (GLuint buffer, GLuint memory, GLuint64 offset);
+#ifdef GL_GLEXT_PROTOTYPES
+GL_APICALL void GL_APIENTRY glGetMemoryObjectDetachedResourcesuivNV (GLuint memory, GLenum pname, GLint first, GLsizei count, GLuint *params);
+GL_APICALL void GL_APIENTRY glResetMemoryObjectParameterNV (GLuint memory, GLenum pname);
+GL_APICALL void GL_APIENTRY glTexAttachMemoryNV (GLenum target, GLuint memory, GLuint64 offset);
+GL_APICALL void GL_APIENTRY glBufferAttachMemoryNV (GLenum target, GLuint memory, GLuint64 offset);
+GL_APICALL void GL_APIENTRY glTextureAttachMemoryNV (GLuint texture, GLuint memory, GLuint64 offset);
+GL_APICALL void GL_APIENTRY glNamedBufferAttachMemoryNV (GLuint buffer, GLuint memory, GLuint64 offset);
+#endif
+#endif /* GL_NV_memory_attachment */
+
#ifndef GL_NV_non_square_matrices
#define GL_NV_non_square_matrices 1
#define GL_FLOAT_MAT2x3_NV 0x8B65
@@ -2681,6 +3004,7 @@ GL_APICALL void GL_APIENTRY glUniformMatrix4x3fvNV (GLint location, GLsizei coun
#ifndef GL_NV_path_rendering
#define GL_NV_path_rendering 1
+typedef double GLdouble;
#define GL_PATH_FORMAT_SVG_NV 0x9070
#define GL_PATH_FORMAT_PS_NV 0x9071
#define GL_STANDARD_FONT_NAME_NV 0x9072
@@ -2891,6 +3215,25 @@ typedef GLenum (GL_APIENTRYP PFNGLPATHGLYPHINDEXARRAYNVPROC) (GLuint firstPathNa
typedef GLenum (GL_APIENTRYP PFNGLPATHMEMORYGLYPHINDEXARRAYNVPROC) (GLuint firstPathName, GLenum fontTarget, GLsizeiptr fontSize, const void *fontData, GLsizei faceIndex, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale);
typedef void (GL_APIENTRYP PFNGLPROGRAMPATHFRAGMENTINPUTGENNVPROC) (GLuint program, GLint location, GLenum genMode, GLint components, const GLfloat *coeffs);
typedef void (GL_APIENTRYP PFNGLGETPROGRAMRESOURCEFVNVPROC) (GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei bufSize, GLsizei *length, GLfloat *params);
+typedef void (GL_APIENTRYP PFNGLMATRIXFRUSTUMEXTPROC) (GLenum mode, GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar);
+typedef void (GL_APIENTRYP PFNGLMATRIXLOADIDENTITYEXTPROC) (GLenum mode);
+typedef void (GL_APIENTRYP PFNGLMATRIXLOADTRANSPOSEFEXTPROC) (GLenum mode, const GLfloat *m);
+typedef void (GL_APIENTRYP PFNGLMATRIXLOADTRANSPOSEDEXTPROC) (GLenum mode, const GLdouble *m);
+typedef void (GL_APIENTRYP PFNGLMATRIXLOADFEXTPROC) (GLenum mode, const GLfloat *m);
+typedef void (GL_APIENTRYP PFNGLMATRIXLOADDEXTPROC) (GLenum mode, const GLdouble *m);
+typedef void (GL_APIENTRYP PFNGLMATRIXMULTTRANSPOSEFEXTPROC) (GLenum mode, const GLfloat *m);
+typedef void (GL_APIENTRYP PFNGLMATRIXMULTTRANSPOSEDEXTPROC) (GLenum mode, const GLdouble *m);
+typedef void (GL_APIENTRYP PFNGLMATRIXMULTFEXTPROC) (GLenum mode, const GLfloat *m);
+typedef void (GL_APIENTRYP PFNGLMATRIXMULTDEXTPROC) (GLenum mode, const GLdouble *m);
+typedef void (GL_APIENTRYP PFNGLMATRIXORTHOEXTPROC) (GLenum mode, GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar);
+typedef void (GL_APIENTRYP PFNGLMATRIXPOPEXTPROC) (GLenum mode);
+typedef void (GL_APIENTRYP PFNGLMATRIXPUSHEXTPROC) (GLenum mode);
+typedef void (GL_APIENTRYP PFNGLMATRIXROTATEFEXTPROC) (GLenum mode, GLfloat angle, GLfloat x, GLfloat y, GLfloat z);
+typedef void (GL_APIENTRYP PFNGLMATRIXROTATEDEXTPROC) (GLenum mode, GLdouble angle, GLdouble x, GLdouble y, GLdouble z);
+typedef void (GL_APIENTRYP PFNGLMATRIXSCALEFEXTPROC) (GLenum mode, GLfloat x, GLfloat y, GLfloat z);
+typedef void (GL_APIENTRYP PFNGLMATRIXSCALEDEXTPROC) (GLenum mode, GLdouble x, GLdouble y, GLdouble z);
+typedef void (GL_APIENTRYP PFNGLMATRIXTRANSLATEFEXTPROC) (GLenum mode, GLfloat x, GLfloat y, GLfloat z);
+typedef void (GL_APIENTRYP PFNGLMATRIXTRANSLATEDEXTPROC) (GLenum mode, GLdouble x, GLdouble y, GLdouble z);
#ifdef GL_GLEXT_PROTOTYPES
GL_APICALL GLuint GL_APIENTRY glGenPathsNV (GLsizei range);
GL_APICALL void GL_APIENTRY glDeletePathsNV (GLuint path, GLsizei range);
@@ -2949,6 +3292,25 @@ GL_APICALL GLenum GL_APIENTRY glPathGlyphIndexArrayNV (GLuint firstPathName, GLe
GL_APICALL GLenum GL_APIENTRY glPathMemoryGlyphIndexArrayNV (GLuint firstPathName, GLenum fontTarget, GLsizeiptr fontSize, const void *fontData, GLsizei faceIndex, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale);
GL_APICALL void GL_APIENTRY glProgramPathFragmentInputGenNV (GLuint program, GLint location, GLenum genMode, GLint components, const GLfloat *coeffs);
GL_APICALL void GL_APIENTRY glGetProgramResourcefvNV (GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei bufSize, GLsizei *length, GLfloat *params);
+GL_APICALL void GL_APIENTRY glMatrixFrustumEXT (GLenum mode, GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar);
+GL_APICALL void GL_APIENTRY glMatrixLoadIdentityEXT (GLenum mode);
+GL_APICALL void GL_APIENTRY glMatrixLoadTransposefEXT (GLenum mode, const GLfloat *m);
+GL_APICALL void GL_APIENTRY glMatrixLoadTransposedEXT (GLenum mode, const GLdouble *m);
+GL_APICALL void GL_APIENTRY glMatrixLoadfEXT (GLenum mode, const GLfloat *m);
+GL_APICALL void GL_APIENTRY glMatrixLoaddEXT (GLenum mode, const GLdouble *m);
+GL_APICALL void GL_APIENTRY glMatrixMultTransposefEXT (GLenum mode, const GLfloat *m);
+GL_APICALL void GL_APIENTRY glMatrixMultTransposedEXT (GLenum mode, const GLdouble *m);
+GL_APICALL void GL_APIENTRY glMatrixMultfEXT (GLenum mode, const GLfloat *m);
+GL_APICALL void GL_APIENTRY glMatrixMultdEXT (GLenum mode, const GLdouble *m);
+GL_APICALL void GL_APIENTRY glMatrixOrthoEXT (GLenum mode, GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar);
+GL_APICALL void GL_APIENTRY glMatrixPopEXT (GLenum mode);
+GL_APICALL void GL_APIENTRY glMatrixPushEXT (GLenum mode);
+GL_APICALL void GL_APIENTRY glMatrixRotatefEXT (GLenum mode, GLfloat angle, GLfloat x, GLfloat y, GLfloat z);
+GL_APICALL void GL_APIENTRY glMatrixRotatedEXT (GLenum mode, GLdouble angle, GLdouble x, GLdouble y, GLdouble z);
+GL_APICALL void GL_APIENTRY glMatrixScalefEXT (GLenum mode, GLfloat x, GLfloat y, GLfloat z);
+GL_APICALL void GL_APIENTRY glMatrixScaledEXT (GLenum mode, GLdouble x, GLdouble y, GLdouble z);
+GL_APICALL void GL_APIENTRY glMatrixTranslatefEXT (GLenum mode, GLfloat x, GLfloat y, GLfloat z);
+GL_APICALL void GL_APIENTRY glMatrixTranslatedEXT (GLenum mode, GLdouble x, GLdouble y, GLdouble z);
#endif
#endif /* GL_NV_path_rendering */
@@ -2957,6 +3319,14 @@ GL_APICALL void GL_APIENTRY glGetProgramResourcefvNV (GLuint program, GLenum pro
#define GL_SHARED_EDGE_NV 0xC0
#endif /* GL_NV_path_rendering_shared_edge */
+#ifndef GL_NV_pixel_buffer_object
+#define GL_NV_pixel_buffer_object 1
+#define GL_PIXEL_PACK_BUFFER_NV 0x88EB
+#define GL_PIXEL_UNPACK_BUFFER_NV 0x88EC
+#define GL_PIXEL_PACK_BUFFER_BINDING_NV 0x88ED
+#define GL_PIXEL_UNPACK_BUFFER_BINDING_NV 0x88EF
+#endif /* GL_NV_pixel_buffer_object */
+
#ifndef GL_NV_polygon_mode
#define GL_NV_polygon_mode 1
#define GL_POLYGON_MODE_NV 0x0B40
@@ -3034,6 +3404,18 @@ GL_APICALL void GL_APIENTRY glResolveDepthValuesNV (void);
#define GL_NV_sample_mask_override_coverage 1
#endif /* GL_NV_sample_mask_override_coverage */
+#ifndef GL_NV_scissor_exclusive
+#define GL_NV_scissor_exclusive 1
+#define GL_SCISSOR_TEST_EXCLUSIVE_NV 0x9555
+#define GL_SCISSOR_BOX_EXCLUSIVE_NV 0x9556
+typedef void (GL_APIENTRYP PFNGLSCISSOREXCLUSIVENVPROC) (GLint x, GLint y, GLsizei width, GLsizei height);
+typedef void (GL_APIENTRYP PFNGLSCISSOREXCLUSIVEARRAYVNVPROC) (GLuint first, GLsizei count, const GLint *v);
+#ifdef GL_GLEXT_PROTOTYPES
+GL_APICALL void GL_APIENTRY glScissorExclusiveNV (GLint x, GLint y, GLsizei width, GLsizei height);
+GL_APICALL void GL_APIENTRY glScissorExclusiveArrayvNV (GLuint first, GLsizei count, const GLint *v);
+#endif
+#endif /* GL_NV_scissor_exclusive */
+
#ifndef GL_NV_shader_atomic_fp16_vector
#define GL_NV_shader_atomic_fp16_vector 1
#endif /* GL_NV_shader_atomic_fp16_vector */
@@ -3052,6 +3434,10 @@ GL_APICALL void GL_APIENTRY glResolveDepthValuesNV (void);
#define GL_SAMPLER_CUBE_SHADOW_NV 0x8DC5
#endif /* GL_NV_shadow_samplers_cube */
+#ifndef GL_NV_stereo_view_rendering
+#define GL_NV_stereo_view_rendering 1
+#endif /* GL_NV_stereo_view_rendering */
+
#ifndef GL_NV_texture_border_clamp
#define GL_NV_texture_border_clamp 1
#define GL_TEXTURE_BORDER_COLOR_NV 0x1004
@@ -3148,6 +3534,10 @@ GL_APICALL void GL_APIENTRY glFramebufferTextureMultisampleMultiviewOVR (GLenum
#endif
#endif /* GL_OVR_multiview_multisampled_render_to_texture */
+#ifndef GL_QCOM_YUV_texture_gather
+#define GL_QCOM_YUV_texture_gather 1
+#endif /* GL_QCOM_YUV_texture_gather */
+
#ifndef GL_QCOM_alpha_test
#define GL_QCOM_alpha_test 1
#define GL_ALPHA_TEST_QCOM 0x0BC0
@@ -3245,6 +3635,38 @@ GL_APICALL void GL_APIENTRY glFramebufferFoveationParametersQCOM (GLuint framebu
#define GL_PERFMON_GLOBAL_MODE_QCOM 0x8FA0
#endif /* GL_QCOM_perfmon_global_mode */
+#ifndef GL_QCOM_shader_framebuffer_fetch_noncoherent
+#define GL_QCOM_shader_framebuffer_fetch_noncoherent 1
+#define GL_FRAMEBUFFER_FETCH_NONCOHERENT_QCOM 0x96A2
+typedef void (GL_APIENTRYP PFNGLFRAMEBUFFERFETCHBARRIERQCOMPROC) (void);
+#ifdef GL_GLEXT_PROTOTYPES
+GL_APICALL void GL_APIENTRY glFramebufferFetchBarrierQCOM (void);
+#endif
+#endif /* GL_QCOM_shader_framebuffer_fetch_noncoherent */
+
+#ifndef GL_QCOM_shader_framebuffer_fetch_rate
+#define GL_QCOM_shader_framebuffer_fetch_rate 1
+#endif /* GL_QCOM_shader_framebuffer_fetch_rate */
+
+#ifndef GL_QCOM_texture_foveated
+#define GL_QCOM_texture_foveated 1
+#define GL_TEXTURE_FOVEATED_FEATURE_BITS_QCOM 0x8BFB
+#define GL_TEXTURE_FOVEATED_MIN_PIXEL_DENSITY_QCOM 0x8BFC
+#define GL_TEXTURE_FOVEATED_FEATURE_QUERY_QCOM 0x8BFD
+#define GL_TEXTURE_FOVEATED_NUM_FOCAL_POINTS_QUERY_QCOM 0x8BFE
+#define GL_FRAMEBUFFER_INCOMPLETE_FOVEATION_QCOM 0x8BFF
+typedef void (GL_APIENTRYP PFNGLTEXTUREFOVEATIONPARAMETERSQCOMPROC) (GLuint texture, GLuint layer, GLuint focalPoint, GLfloat focalX, GLfloat focalY, GLfloat gainX, GLfloat gainY, GLfloat foveaArea);
+#ifdef GL_GLEXT_PROTOTYPES
+GL_APICALL void GL_APIENTRY glTextureFoveationParametersQCOM (GLuint texture, GLuint layer, GLuint focalPoint, GLfloat focalX, GLfloat focalY, GLfloat gainX, GLfloat gainY, GLfloat foveaArea);
+#endif
+#endif /* GL_QCOM_texture_foveated */
+
+#ifndef GL_QCOM_texture_foveated_subsampled_layout
+#define GL_QCOM_texture_foveated_subsampled_layout 1
+#define GL_FOVEATION_SUBSAMPLED_LAYOUT_METHOD_BIT_QCOM 0x00000004
+#define GL_MAX_SHADER_SUBSAMPLED_IMAGE_UNITS_QCOM 0x8FA1
+#endif /* GL_QCOM_texture_foveated_subsampled_layout */
+
#ifndef GL_QCOM_tiled_rendering
#define GL_QCOM_tiled_rendering 1
#define GL_COLOR_BUFFER_BIT0_QCOM 0x00000001
diff --git a/src/gui/opengl/qopenglext.h b/src/gui/opengl/qopenglext.h
index 63873476e4..e3f9205619 100644
--- a/src/gui/opengl/qopenglext.h
+++ b/src/gui/opengl/qopenglext.h
@@ -12,7 +12,7 @@ extern "C" {
#endif
/*
-** Copyright (c) 2013-2017 The Khronos Group Inc.
+** Copyright (c) 2013-2018 The Khronos Group Inc.
**
** Permission is hereby granted, free of charge, to any person obtaining a
** copy of this software and/or associated documentation files (the
@@ -57,7 +57,7 @@ extern "C" {
#define GLAPI extern
#endif
-#define GL_GLEXT_VERSION 20170325
+#define GL_GLEXT_VERSION 20190228
/* Generated C header for:
* API: gl
@@ -359,15 +359,17 @@ GLAPI void APIENTRY glMultTransposeMatrixd (const GLdouble *m);
#define GL_TEXTURE_FILTER_CONTROL 0x8500
#define GL_DEPTH_TEXTURE_MODE 0x884B
#define GL_COMPARE_R_TO_TEXTURE 0x884E
-#define GL_FUNC_ADD 0x8006
-#define GL_FUNC_SUBTRACT 0x800A
-#define GL_FUNC_REVERSE_SUBTRACT 0x800B
-#define GL_MIN 0x8007
-#define GL_MAX 0x8008
+#define GL_BLEND_COLOR 0x8005
+#define GL_BLEND_EQUATION 0x8009
#define GL_CONSTANT_COLOR 0x8001
#define GL_ONE_MINUS_CONSTANT_COLOR 0x8002
#define GL_CONSTANT_ALPHA 0x8003
#define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004
+#define GL_FUNC_ADD 0x8006
+#define GL_FUNC_REVERSE_SUBTRACT 0x800B
+#define GL_FUNC_SUBTRACT 0x800A
+#define GL_MIN 0x8007
+#define GL_MAX 0x8008
typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEPROC) (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha);
typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSPROC) (GLenum mode, const GLint *first, const GLsizei *count, GLsizei drawcount);
typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSPROC) (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount);
@@ -2872,6 +2874,42 @@ GLAPI void APIENTRY glTextureBarrier (void);
#endif
#endif /* GL_VERSION_4_5 */
+#ifndef GL_VERSION_4_6
+#define GL_VERSION_4_6 1
+#define GL_SHADER_BINARY_FORMAT_SPIR_V 0x9551
+#define GL_SPIR_V_BINARY 0x9552
+#define GL_PARAMETER_BUFFER 0x80EE
+#define GL_PARAMETER_BUFFER_BINDING 0x80EF
+#define GL_CONTEXT_FLAG_NO_ERROR_BIT 0x00000008
+#define GL_VERTICES_SUBMITTED 0x82EE
+#define GL_PRIMITIVES_SUBMITTED 0x82EF
+#define GL_VERTEX_SHADER_INVOCATIONS 0x82F0
+#define GL_TESS_CONTROL_SHADER_PATCHES 0x82F1
+#define GL_TESS_EVALUATION_SHADER_INVOCATIONS 0x82F2
+#define GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED 0x82F3
+#define GL_FRAGMENT_SHADER_INVOCATIONS 0x82F4
+#define GL_COMPUTE_SHADER_INVOCATIONS 0x82F5
+#define GL_CLIPPING_INPUT_PRIMITIVES 0x82F6
+#define GL_CLIPPING_OUTPUT_PRIMITIVES 0x82F7
+#define GL_POLYGON_OFFSET_CLAMP 0x8E1B
+#define GL_SPIR_V_EXTENSIONS 0x9553
+#define GL_NUM_SPIR_V_EXTENSIONS 0x9554
+#define GL_TEXTURE_MAX_ANISOTROPY 0x84FE
+#define GL_MAX_TEXTURE_MAX_ANISOTROPY 0x84FF
+#define GL_TRANSFORM_FEEDBACK_OVERFLOW 0x82EC
+#define GL_TRANSFORM_FEEDBACK_STREAM_OVERFLOW 0x82ED
+typedef void (APIENTRYP PFNGLSPECIALIZESHADERPROC) (GLuint shader, const GLchar *pEntryPoint, GLuint numSpecializationConstants, const GLuint *pConstantIndex, const GLuint *pConstantValue);
+typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTCOUNTPROC) (GLenum mode, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride);
+typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTPROC) (GLenum mode, GLenum type, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride);
+typedef void (APIENTRYP PFNGLPOLYGONOFFSETCLAMPPROC) (GLfloat factor, GLfloat units, GLfloat clamp);
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glSpecializeShader (GLuint shader, const GLchar *pEntryPoint, GLuint numSpecializationConstants, const GLuint *pConstantIndex, const GLuint *pConstantValue);
+GLAPI void APIENTRY glMultiDrawArraysIndirectCount (GLenum mode, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride);
+GLAPI void APIENTRY glMultiDrawElementsIndirectCount (GLenum mode, GLenum type, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride);
+GLAPI void APIENTRY glPolygonOffsetClamp (GLfloat factor, GLfloat units, GLfloat clamp);
+#endif
+#endif /* GL_VERSION_4_6 */
+
#ifndef GL_ARB_ES2_compatibility
#define GL_ARB_ES2_compatibility 1
#endif /* GL_ARB_ES2_compatibility */
@@ -3475,8 +3513,6 @@ typedef unsigned short GLhalfARB;
#ifndef GL_ARB_imaging
#define GL_ARB_imaging 1
-#define GL_BLEND_COLOR 0x8005
-#define GL_BLEND_EQUATION 0x8009
#define GL_CONVOLUTION_1D 0x8010
#define GL_CONVOLUTION_2D 0x8011
#define GL_SEPARABLE_2D 0x8012
@@ -3613,11 +3649,11 @@ GLAPI void APIENTRY glResetMinmax (GLenum target);
#define GL_ARB_indirect_parameters 1
#define GL_PARAMETER_BUFFER_ARB 0x80EE
#define GL_PARAMETER_BUFFER_BINDING_ARB 0x80EF
-typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTCOUNTARBPROC) (GLenum mode, GLintptr indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride);
-typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTARBPROC) (GLenum mode, GLenum type, GLintptr indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride);
+typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTCOUNTARBPROC) (GLenum mode, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride);
+typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTARBPROC) (GLenum mode, GLenum type, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride);
#ifdef GL_GLEXT_PROTOTYPES
-GLAPI void APIENTRY glMultiDrawArraysIndirectCountARB (GLenum mode, GLintptr indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride);
-GLAPI void APIENTRY glMultiDrawElementsIndirectCountARB (GLenum mode, GLenum type, GLintptr indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride);
+GLAPI void APIENTRY glMultiDrawArraysIndirectCountARB (GLenum mode, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride);
+GLAPI void APIENTRY glMultiDrawElementsIndirectCountARB (GLenum mode, GLenum type, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride);
#endif
#endif /* GL_ARB_indirect_parameters */
@@ -3637,6 +3673,25 @@ GLAPI void APIENTRY glVertexAttribDivisorARB (GLuint index, GLuint divisor);
#ifndef GL_ARB_internalformat_query2
#define GL_ARB_internalformat_query2 1
#define GL_SRGB_DECODE_ARB 0x8299
+#define GL_VIEW_CLASS_EAC_R11 0x9383
+#define GL_VIEW_CLASS_EAC_RG11 0x9384
+#define GL_VIEW_CLASS_ETC2_RGB 0x9385
+#define GL_VIEW_CLASS_ETC2_RGBA 0x9386
+#define GL_VIEW_CLASS_ETC2_EAC_RGBA 0x9387
+#define GL_VIEW_CLASS_ASTC_4x4_RGBA 0x9388
+#define GL_VIEW_CLASS_ASTC_5x4_RGBA 0x9389
+#define GL_VIEW_CLASS_ASTC_5x5_RGBA 0x938A
+#define GL_VIEW_CLASS_ASTC_6x5_RGBA 0x938B
+#define GL_VIEW_CLASS_ASTC_6x6_RGBA 0x938C
+#define GL_VIEW_CLASS_ASTC_8x5_RGBA 0x938D
+#define GL_VIEW_CLASS_ASTC_8x6_RGBA 0x938E
+#define GL_VIEW_CLASS_ASTC_8x8_RGBA 0x938F
+#define GL_VIEW_CLASS_ASTC_10x5_RGBA 0x9390
+#define GL_VIEW_CLASS_ASTC_10x6_RGBA 0x9391
+#define GL_VIEW_CLASS_ASTC_10x8_RGBA 0x9392
+#define GL_VIEW_CLASS_ASTC_10x10_RGBA 0x9393
+#define GL_VIEW_CLASS_ASTC_12x10_RGBA 0x9394
+#define GL_VIEW_CLASS_ASTC_12x12_RGBA 0x9395
#endif /* GL_ARB_internalformat_query2 */
#ifndef GL_ARB_invalidate_subdata
@@ -3894,6 +3949,10 @@ GLAPI void APIENTRY glPointParameterfvARB (GLenum pname, const GLfloat *params);
#define GL_COORD_REPLACE_ARB 0x8862
#endif /* GL_ARB_point_sprite */
+#ifndef GL_ARB_polygon_offset_clamp
+#define GL_ARB_polygon_offset_clamp 1
+#endif /* GL_ARB_polygon_offset_clamp */
+
#ifndef GL_ARB_post_depth_coverage
#define GL_ARB_post_depth_coverage 1
#endif /* GL_ARB_post_depth_coverage */
@@ -4292,6 +4351,10 @@ GLAPI void APIENTRY glTexPageCommitmentARB (GLenum target, GLint level, GLint xo
#define GL_ARB_sparse_texture_clamp 1
#endif /* GL_ARB_sparse_texture_clamp */
+#ifndef GL_ARB_spirv_extensions
+#define GL_ARB_spirv_extensions 1
+#endif /* GL_ARB_spirv_extensions */
+
#ifndef GL_ARB_stencil_texturing
#define GL_ARB_stencil_texturing 1
#endif /* GL_ARB_stencil_texturing */
@@ -4444,6 +4507,10 @@ GLAPI void APIENTRY glGetCompressedTexImageARB (GLenum target, GLint level, void
#define GL_DOT3_RGBA_ARB 0x86AF
#endif /* GL_ARB_texture_env_dot3 */
+#ifndef GL_ARB_texture_filter_anisotropic
+#define GL_ARB_texture_filter_anisotropic 1
+#endif /* GL_ARB_texture_filter_anisotropic */
+
#ifndef GL_ARB_texture_filter_minmax
#define GL_ARB_texture_filter_minmax 1
#define GL_TEXTURE_REDUCTION_MODE_ARB 0x9366
@@ -4949,6 +5016,16 @@ GLAPI void APIENTRY glBlendBarrierKHR (void);
#define GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR 0x00000008
#endif /* GL_KHR_no_error */
+#ifndef GL_KHR_parallel_shader_compile
+#define GL_KHR_parallel_shader_compile 1
+#define GL_MAX_SHADER_COMPILER_THREADS_KHR 0x91B0
+#define GL_COMPLETION_STATUS_KHR 0x91B1
+typedef void (APIENTRYP PFNGLMAXSHADERCOMPILERTHREADSKHRPROC) (GLuint count);
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glMaxShaderCompilerThreadsKHR (GLuint count);
+#endif
+#endif /* GL_KHR_parallel_shader_compile */
+
#ifndef GL_KHR_robust_buffer_access_behavior
#define GL_KHR_robust_buffer_access_behavior 1
#endif /* GL_KHR_robust_buffer_access_behavior */
@@ -5389,6 +5466,22 @@ GLAPI void APIENTRY glBlendEquationSeparateIndexedAMD (GLuint buf, GLenum modeRG
#endif
#endif /* GL_AMD_draw_buffers_blend */
+#ifndef GL_AMD_framebuffer_multisample_advanced
+#define GL_AMD_framebuffer_multisample_advanced 1
+#define GL_RENDERBUFFER_STORAGE_SAMPLES_AMD 0x91B2
+#define GL_MAX_COLOR_FRAMEBUFFER_SAMPLES_AMD 0x91B3
+#define GL_MAX_COLOR_FRAMEBUFFER_STORAGE_SAMPLES_AMD 0x91B4
+#define GL_MAX_DEPTH_STENCIL_FRAMEBUFFER_SAMPLES_AMD 0x91B5
+#define GL_NUM_SUPPORTED_MULTISAMPLE_MODES_AMD 0x91B6
+#define GL_SUPPORTED_MULTISAMPLE_MODES_AMD 0x91B7
+typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLEADVANCEDAMDPROC) (GLenum target, GLsizei samples, GLsizei storageSamples, GLenum internalformat, GLsizei width, GLsizei height);
+typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEADVANCEDAMDPROC) (GLuint renderbuffer, GLsizei samples, GLsizei storageSamples, GLenum internalformat, GLsizei width, GLsizei height);
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glRenderbufferStorageMultisampleAdvancedAMD (GLenum target, GLsizei samples, GLsizei storageSamples, GLenum internalformat, GLsizei width, GLsizei height);
+GLAPI void APIENTRY glNamedRenderbufferStorageMultisampleAdvancedAMD (GLuint renderbuffer, GLsizei samples, GLsizei storageSamples, GLenum internalformat, GLsizei width, GLsizei height);
+#endif
+#endif /* GL_AMD_framebuffer_multisample_advanced */
+
#ifndef GL_AMD_framebuffer_sample_positions
#define GL_AMD_framebuffer_sample_positions 1
#define GL_SUBSAMPLE_DISTANCE_AMD 0x883F
@@ -5428,6 +5521,10 @@ GLAPI void APIENTRY glGetNamedFramebufferParameterfvAMD (GLuint framebuffer, GLe
#define GL_FLOAT16_MAT4x3_AMD 0x91CD
#endif /* GL_AMD_gpu_shader_half_float */
+#ifndef GL_AMD_gpu_shader_int16
+#define GL_AMD_gpu_shader_int16 1
+#endif /* GL_AMD_gpu_shader_int16 */
+
#ifndef GL_AMD_gpu_shader_int64
#define GL_AMD_gpu_shader_int64 1
typedef int64_t GLint64EXT;
@@ -5649,6 +5746,14 @@ GLAPI void APIENTRY glSetMultisamplefvAMD (GLenum pname, GLuint index, const GLf
#define GL_AMD_shader_explicit_vertex_parameter 1
#endif /* GL_AMD_shader_explicit_vertex_parameter */
+#ifndef GL_AMD_shader_gpu_shader_half_float_fetch
+#define GL_AMD_shader_gpu_shader_half_float_fetch 1
+#endif /* GL_AMD_shader_gpu_shader_half_float_fetch */
+
+#ifndef GL_AMD_shader_image_load_store_lod
+#define GL_AMD_shader_image_load_store_lod 1
+#endif /* GL_AMD_shader_image_load_store_lod */
+
#ifndef GL_AMD_shader_stencil_export
#define GL_AMD_shader_stencil_export 1
#endif /* GL_AMD_shader_stencil_export */
@@ -5688,6 +5793,10 @@ GLAPI void APIENTRY glStencilOpValueAMD (GLenum face, GLuint value);
#endif
#endif /* GL_AMD_stencil_operation_extended */
+#ifndef GL_AMD_texture_gather_bias_lod
+#define GL_AMD_texture_gather_bias_lod 1
+#endif /* GL_AMD_texture_gather_bias_lod */
+
#ifndef GL_AMD_texture_texture4
#define GL_AMD_texture_texture4 1
#endif /* GL_AMD_texture_texture4 */
@@ -6388,6 +6497,17 @@ GLAPI void APIENTRY glVertexBlendEnvfATI (GLenum pname, GLfloat param);
#define GL_422_REV_AVERAGE_EXT 0x80CF
#endif /* GL_EXT_422_pixels */
+#ifndef GL_EXT_EGL_image_storage
+#define GL_EXT_EGL_image_storage 1
+typedef void *GLeglImageOES;
+typedef void (APIENTRYP PFNGLEGLIMAGETARGETTEXSTORAGEEXTPROC) (GLenum target, GLeglImageOES image, const GLint* attrib_list);
+typedef void (APIENTRYP PFNGLEGLIMAGETARGETTEXTURESTORAGEEXTPROC) (GLuint texture, GLeglImageOES image, const GLint* attrib_list);
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glEGLImageTargetTexStorageEXT (GLenum target, GLeglImageOES image, const GLint* attrib_list);
+GLAPI void APIENTRY glEGLImageTargetTextureStorageEXT (GLuint texture, GLeglImageOES image, const GLint* attrib_list);
+#endif
+#endif /* GL_EXT_EGL_image_storage */
+
#ifndef GL_EXT_abgr
#define GL_EXT_abgr 1
#define GL_ABGR_EXT 0x8000
@@ -7239,6 +7359,17 @@ GLAPI void APIENTRY glDrawRangeElementsEXT (GLenum mode, GLuint start, GLuint en
#endif
#endif /* GL_EXT_draw_range_elements */
+#ifndef GL_EXT_external_buffer
+#define GL_EXT_external_buffer 1
+typedef void *GLeglClientBufferEXT;
+typedef void (APIENTRYP PFNGLBUFFERSTORAGEEXTERNALEXTPROC) (GLenum target, GLintptr offset, GLsizeiptr size, GLeglClientBufferEXT clientBuffer, GLbitfield flags);
+typedef void (APIENTRYP PFNGLNAMEDBUFFERSTORAGEEXTERNALEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, GLeglClientBufferEXT clientBuffer, GLbitfield flags);
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glBufferStorageExternalEXT (GLenum target, GLintptr offset, GLsizeiptr size, GLeglClientBufferEXT clientBuffer, GLbitfield flags);
+GLAPI void APIENTRY glNamedBufferStorageExternalEXT (GLuint buffer, GLintptr offset, GLsizeiptr size, GLeglClientBufferEXT clientBuffer, GLbitfield flags);
+#endif
+#endif /* GL_EXT_external_buffer */
+
#ifndef GL_EXT_fog_coord
#define GL_EXT_fog_coord 1
#define GL_FOG_COORDINATE_SOURCE_EXT 0x8450
@@ -7582,6 +7713,89 @@ GLAPI void APIENTRY glTextureMaterialEXT (GLenum face, GLenum mode);
#endif
#endif /* GL_EXT_light_texture */
+#ifndef GL_EXT_memory_object
+#define GL_EXT_memory_object 1
+#define GL_TEXTURE_TILING_EXT 0x9580
+#define GL_DEDICATED_MEMORY_OBJECT_EXT 0x9581
+#define GL_PROTECTED_MEMORY_OBJECT_EXT 0x959B
+#define GL_NUM_TILING_TYPES_EXT 0x9582
+#define GL_TILING_TYPES_EXT 0x9583
+#define GL_OPTIMAL_TILING_EXT 0x9584
+#define GL_LINEAR_TILING_EXT 0x9585
+#define GL_NUM_DEVICE_UUIDS_EXT 0x9596
+#define GL_DEVICE_UUID_EXT 0x9597
+#define GL_DRIVER_UUID_EXT 0x9598
+#define GL_UUID_SIZE_EXT 16
+typedef void (APIENTRYP PFNGLGETUNSIGNEDBYTEVEXTPROC) (GLenum pname, GLubyte *data);
+typedef void (APIENTRYP PFNGLGETUNSIGNEDBYTEI_VEXTPROC) (GLenum target, GLuint index, GLubyte *data);
+typedef void (APIENTRYP PFNGLDELETEMEMORYOBJECTSEXTPROC) (GLsizei n, const GLuint *memoryObjects);
+typedef GLboolean (APIENTRYP PFNGLISMEMORYOBJECTEXTPROC) (GLuint memoryObject);
+typedef void (APIENTRYP PFNGLCREATEMEMORYOBJECTSEXTPROC) (GLsizei n, GLuint *memoryObjects);
+typedef void (APIENTRYP PFNGLMEMORYOBJECTPARAMETERIVEXTPROC) (GLuint memoryObject, GLenum pname, const GLint *params);
+typedef void (APIENTRYP PFNGLGETMEMORYOBJECTPARAMETERIVEXTPROC) (GLuint memoryObject, GLenum pname, GLint *params);
+typedef void (APIENTRYP PFNGLTEXSTORAGEMEM2DEXTPROC) (GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLuint memory, GLuint64 offset);
+typedef void (APIENTRYP PFNGLTEXSTORAGEMEM2DMULTISAMPLEEXTPROC) (GLenum target, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations, GLuint memory, GLuint64 offset);
+typedef void (APIENTRYP PFNGLTEXSTORAGEMEM3DEXTPROC) (GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLuint memory, GLuint64 offset);
+typedef void (APIENTRYP PFNGLTEXSTORAGEMEM3DMULTISAMPLEEXTPROC) (GLenum target, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations, GLuint memory, GLuint64 offset);
+typedef void (APIENTRYP PFNGLBUFFERSTORAGEMEMEXTPROC) (GLenum target, GLsizeiptr size, GLuint memory, GLuint64 offset);
+typedef void (APIENTRYP PFNGLTEXTURESTORAGEMEM2DEXTPROC) (GLuint texture, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLuint memory, GLuint64 offset);
+typedef void (APIENTRYP PFNGLTEXTURESTORAGEMEM2DMULTISAMPLEEXTPROC) (GLuint texture, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations, GLuint memory, GLuint64 offset);
+typedef void (APIENTRYP PFNGLTEXTURESTORAGEMEM3DEXTPROC) (GLuint texture, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLuint memory, GLuint64 offset);
+typedef void (APIENTRYP PFNGLTEXTURESTORAGEMEM3DMULTISAMPLEEXTPROC) (GLuint texture, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations, GLuint memory, GLuint64 offset);
+typedef void (APIENTRYP PFNGLNAMEDBUFFERSTORAGEMEMEXTPROC) (GLuint buffer, GLsizeiptr size, GLuint memory, GLuint64 offset);
+typedef void (APIENTRYP PFNGLTEXSTORAGEMEM1DEXTPROC) (GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width, GLuint memory, GLuint64 offset);
+typedef void (APIENTRYP PFNGLTEXTURESTORAGEMEM1DEXTPROC) (GLuint texture, GLsizei levels, GLenum internalFormat, GLsizei width, GLuint memory, GLuint64 offset);
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glGetUnsignedBytevEXT (GLenum pname, GLubyte *data);
+GLAPI void APIENTRY glGetUnsignedBytei_vEXT (GLenum target, GLuint index, GLubyte *data);
+GLAPI void APIENTRY glDeleteMemoryObjectsEXT (GLsizei n, const GLuint *memoryObjects);
+GLAPI GLboolean APIENTRY glIsMemoryObjectEXT (GLuint memoryObject);
+GLAPI void APIENTRY glCreateMemoryObjectsEXT (GLsizei n, GLuint *memoryObjects);
+GLAPI void APIENTRY glMemoryObjectParameterivEXT (GLuint memoryObject, GLenum pname, const GLint *params);
+GLAPI void APIENTRY glGetMemoryObjectParameterivEXT (GLuint memoryObject, GLenum pname, GLint *params);
+GLAPI void APIENTRY glTexStorageMem2DEXT (GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLuint memory, GLuint64 offset);
+GLAPI void APIENTRY glTexStorageMem2DMultisampleEXT (GLenum target, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations, GLuint memory, GLuint64 offset);
+GLAPI void APIENTRY glTexStorageMem3DEXT (GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLuint memory, GLuint64 offset);
+GLAPI void APIENTRY glTexStorageMem3DMultisampleEXT (GLenum target, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations, GLuint memory, GLuint64 offset);
+GLAPI void APIENTRY glBufferStorageMemEXT (GLenum target, GLsizeiptr size, GLuint memory, GLuint64 offset);
+GLAPI void APIENTRY glTextureStorageMem2DEXT (GLuint texture, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLuint memory, GLuint64 offset);
+GLAPI void APIENTRY glTextureStorageMem2DMultisampleEXT (GLuint texture, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations, GLuint memory, GLuint64 offset);
+GLAPI void APIENTRY glTextureStorageMem3DEXT (GLuint texture, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLuint memory, GLuint64 offset);
+GLAPI void APIENTRY glTextureStorageMem3DMultisampleEXT (GLuint texture, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations, GLuint memory, GLuint64 offset);
+GLAPI void APIENTRY glNamedBufferStorageMemEXT (GLuint buffer, GLsizeiptr size, GLuint memory, GLuint64 offset);
+GLAPI void APIENTRY glTexStorageMem1DEXT (GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width, GLuint memory, GLuint64 offset);
+GLAPI void APIENTRY glTextureStorageMem1DEXT (GLuint texture, GLsizei levels, GLenum internalFormat, GLsizei width, GLuint memory, GLuint64 offset);
+#endif
+#endif /* GL_EXT_memory_object */
+
+#ifndef GL_EXT_memory_object_fd
+#define GL_EXT_memory_object_fd 1
+#define GL_HANDLE_TYPE_OPAQUE_FD_EXT 0x9586
+typedef void (APIENTRYP PFNGLIMPORTMEMORYFDEXTPROC) (GLuint memory, GLuint64 size, GLenum handleType, GLint fd);
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glImportMemoryFdEXT (GLuint memory, GLuint64 size, GLenum handleType, GLint fd);
+#endif
+#endif /* GL_EXT_memory_object_fd */
+
+#ifndef GL_EXT_memory_object_win32
+#define GL_EXT_memory_object_win32 1
+#define GL_HANDLE_TYPE_OPAQUE_WIN32_EXT 0x9587
+#define GL_HANDLE_TYPE_OPAQUE_WIN32_KMT_EXT 0x9588
+#define GL_DEVICE_LUID_EXT 0x9599
+#define GL_DEVICE_NODE_MASK_EXT 0x959A
+#define GL_LUID_SIZE_EXT 8
+#define GL_HANDLE_TYPE_D3D12_TILEPOOL_EXT 0x9589
+#define GL_HANDLE_TYPE_D3D12_RESOURCE_EXT 0x958A
+#define GL_HANDLE_TYPE_D3D11_IMAGE_EXT 0x958B
+#define GL_HANDLE_TYPE_D3D11_IMAGE_KMT_EXT 0x958C
+typedef void (APIENTRYP PFNGLIMPORTMEMORYWIN32HANDLEEXTPROC) (GLuint memory, GLuint64 size, GLenum handleType, void *handle);
+typedef void (APIENTRYP PFNGLIMPORTMEMORYWIN32NAMEEXTPROC) (GLuint memory, GLuint64 size, GLenum handleType, const void *name);
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glImportMemoryWin32HandleEXT (GLuint memory, GLuint64 size, GLenum handleType, void *handle);
+GLAPI void APIENTRY glImportMemoryWin32NameEXT (GLuint memory, GLuint64 size, GLenum handleType, const void *name);
+#endif
+#endif /* GL_EXT_memory_object_win32 */
+
#ifndef GL_EXT_misc_attribute
#define GL_EXT_misc_attribute 1
#endif /* GL_EXT_misc_attribute */
@@ -7823,6 +8037,55 @@ GLAPI void APIENTRY glSecondaryColorPointerEXT (GLint size, GLenum type, GLsizei
#endif
#endif /* GL_EXT_secondary_color */
+#ifndef GL_EXT_semaphore
+#define GL_EXT_semaphore 1
+#define GL_LAYOUT_GENERAL_EXT 0x958D
+#define GL_LAYOUT_COLOR_ATTACHMENT_EXT 0x958E
+#define GL_LAYOUT_DEPTH_STENCIL_ATTACHMENT_EXT 0x958F
+#define GL_LAYOUT_DEPTH_STENCIL_READ_ONLY_EXT 0x9590
+#define GL_LAYOUT_SHADER_READ_ONLY_EXT 0x9591
+#define GL_LAYOUT_TRANSFER_SRC_EXT 0x9592
+#define GL_LAYOUT_TRANSFER_DST_EXT 0x9593
+#define GL_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_EXT 0x9530
+#define GL_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_EXT 0x9531
+typedef void (APIENTRYP PFNGLGENSEMAPHORESEXTPROC) (GLsizei n, GLuint *semaphores);
+typedef void (APIENTRYP PFNGLDELETESEMAPHORESEXTPROC) (GLsizei n, const GLuint *semaphores);
+typedef GLboolean (APIENTRYP PFNGLISSEMAPHOREEXTPROC) (GLuint semaphore);
+typedef void (APIENTRYP PFNGLSEMAPHOREPARAMETERUI64VEXTPROC) (GLuint semaphore, GLenum pname, const GLuint64 *params);
+typedef void (APIENTRYP PFNGLGETSEMAPHOREPARAMETERUI64VEXTPROC) (GLuint semaphore, GLenum pname, GLuint64 *params);
+typedef void (APIENTRYP PFNGLWAITSEMAPHOREEXTPROC) (GLuint semaphore, GLuint numBufferBarriers, const GLuint *buffers, GLuint numTextureBarriers, const GLuint *textures, const GLenum *srcLayouts);
+typedef void (APIENTRYP PFNGLSIGNALSEMAPHOREEXTPROC) (GLuint semaphore, GLuint numBufferBarriers, const GLuint *buffers, GLuint numTextureBarriers, const GLuint *textures, const GLenum *dstLayouts);
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glGenSemaphoresEXT (GLsizei n, GLuint *semaphores);
+GLAPI void APIENTRY glDeleteSemaphoresEXT (GLsizei n, const GLuint *semaphores);
+GLAPI GLboolean APIENTRY glIsSemaphoreEXT (GLuint semaphore);
+GLAPI void APIENTRY glSemaphoreParameterui64vEXT (GLuint semaphore, GLenum pname, const GLuint64 *params);
+GLAPI void APIENTRY glGetSemaphoreParameterui64vEXT (GLuint semaphore, GLenum pname, GLuint64 *params);
+GLAPI void APIENTRY glWaitSemaphoreEXT (GLuint semaphore, GLuint numBufferBarriers, const GLuint *buffers, GLuint numTextureBarriers, const GLuint *textures, const GLenum *srcLayouts);
+GLAPI void APIENTRY glSignalSemaphoreEXT (GLuint semaphore, GLuint numBufferBarriers, const GLuint *buffers, GLuint numTextureBarriers, const GLuint *textures, const GLenum *dstLayouts);
+#endif
+#endif /* GL_EXT_semaphore */
+
+#ifndef GL_EXT_semaphore_fd
+#define GL_EXT_semaphore_fd 1
+typedef void (APIENTRYP PFNGLIMPORTSEMAPHOREFDEXTPROC) (GLuint semaphore, GLenum handleType, GLint fd);
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glImportSemaphoreFdEXT (GLuint semaphore, GLenum handleType, GLint fd);
+#endif
+#endif /* GL_EXT_semaphore_fd */
+
+#ifndef GL_EXT_semaphore_win32
+#define GL_EXT_semaphore_win32 1
+#define GL_HANDLE_TYPE_D3D12_FENCE_EXT 0x9594
+#define GL_D3D12_FENCE_VALUE_EXT 0x9595
+typedef void (APIENTRYP PFNGLIMPORTSEMAPHOREWIN32HANDLEEXTPROC) (GLuint semaphore, GLenum handleType, void *handle);
+typedef void (APIENTRYP PFNGLIMPORTSEMAPHOREWIN32NAMEEXTPROC) (GLuint semaphore, GLenum handleType, const void *name);
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glImportSemaphoreWin32HandleEXT (GLuint semaphore, GLenum handleType, void *handle);
+GLAPI void APIENTRY glImportSemaphoreWin32NameEXT (GLuint semaphore, GLenum handleType, const void *name);
+#endif
+#endif /* GL_EXT_semaphore_win32 */
+
#ifndef GL_EXT_separate_shader_objects
#define GL_EXT_separate_shader_objects 1
#define GL_ACTIVE_PROGRAM_EXT 0x8B8D
@@ -7843,6 +8106,19 @@ GLAPI GLuint APIENTRY glCreateShaderProgramEXT (GLenum type, const GLchar *strin
#define GL_SEPARATE_SPECULAR_COLOR_EXT 0x81FA
#endif /* GL_EXT_separate_specular_color */
+#ifndef GL_EXT_shader_framebuffer_fetch
+#define GL_EXT_shader_framebuffer_fetch 1
+#define GL_FRAGMENT_SHADER_DISCARDS_SAMPLES_EXT 0x8A52
+#endif /* GL_EXT_shader_framebuffer_fetch */
+
+#ifndef GL_EXT_shader_framebuffer_fetch_non_coherent
+#define GL_EXT_shader_framebuffer_fetch_non_coherent 1
+typedef void (APIENTRYP PFNGLFRAMEBUFFERFETCHBARRIEREXTPROC) (void);
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glFramebufferFetchBarrierEXT (void);
+#endif
+#endif /* GL_EXT_shader_framebuffer_fetch_non_coherent */
+
#ifndef GL_EXT_shader_image_load_formatted
#define GL_EXT_shader_image_load_formatted 1
#endif /* GL_EXT_shader_image_load_formatted */
@@ -8143,6 +8419,8 @@ GLAPI void APIENTRY glTexBufferEXT (GLenum target, GLenum internalformat, GLuint
#ifndef GL_EXT_texture_filter_minmax
#define GL_EXT_texture_filter_minmax 1
+#define GL_TEXTURE_REDUCTION_MODE_EXT 0x9366
+#define GL_WEIGHTED_AVERAGE_EXT 0x9367
#endif /* GL_EXT_texture_filter_minmax */
#ifndef GL_EXT_texture_integer
@@ -8277,6 +8555,11 @@ GLAPI void APIENTRY glTextureNormalEXT (GLenum mode);
#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT 0x8C4F
#endif /* GL_EXT_texture_sRGB */
+#ifndef GL_EXT_texture_sRGB_R8
+#define GL_EXT_texture_sRGB_R8 1
+#define GL_SR8_EXT 0x8FBD
+#endif /* GL_EXT_texture_sRGB_R8 */
+
#ifndef GL_EXT_texture_sRGB_decode
#define GL_EXT_texture_sRGB_decode 1
#define GL_TEXTURE_SRGB_DECODE_EXT 0x8A48
@@ -8689,6 +8972,16 @@ GLAPI void APIENTRY glVertexWeightPointerEXT (GLint size, GLenum type, GLsizei s
#endif
#endif /* GL_EXT_vertex_weighting */
+#ifndef GL_EXT_win32_keyed_mutex
+#define GL_EXT_win32_keyed_mutex 1
+typedef GLboolean (APIENTRYP PFNGLACQUIREKEYEDMUTEXWIN32EXTPROC) (GLuint memory, GLuint64 key, GLuint timeout);
+typedef GLboolean (APIENTRYP PFNGLRELEASEKEYEDMUTEXWIN32EXTPROC) (GLuint memory, GLuint64 key);
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI GLboolean APIENTRY glAcquireKeyedMutexWin32EXT (GLuint memory, GLuint64 key, GLuint timeout);
+GLAPI GLboolean APIENTRY glReleaseKeyedMutexWin32EXT (GLuint memory, GLuint64 key);
+#endif
+#endif /* GL_EXT_win32_keyed_mutex */
+
#ifndef GL_EXT_window_rectangles
#define GL_EXT_window_rectangles 1
#define GL_INCLUSIVE_EXT 0x8F10
@@ -8880,6 +9173,11 @@ GLAPI void APIENTRY glBlendFuncSeparateINGR (GLenum sfactorRGB, GLenum dfactorRG
#define GL_INTERLACE_READ_INGR 0x8568
#endif /* GL_INGR_interlace_read */
+#ifndef GL_INTEL_blackhole_render
+#define GL_INTEL_blackhole_render 1
+#define GL_BLACKHOLE_RENDER_INTEL 0x83FC
+#endif /* GL_INTEL_blackhole_render */
+
#ifndef GL_INTEL_conservative_rasterization
#define GL_INTEL_conservative_rasterization 1
#define GL_CONSERVATIVE_RASTERIZATION_INTEL 0x83FE
@@ -8961,7 +9259,7 @@ typedef void (APIENTRYP PFNGLENDPERFQUERYINTELPROC) (GLuint queryHandle);
typedef void (APIENTRYP PFNGLGETFIRSTPERFQUERYIDINTELPROC) (GLuint *queryId);
typedef void (APIENTRYP PFNGLGETNEXTPERFQUERYIDINTELPROC) (GLuint queryId, GLuint *nextQueryId);
typedef void (APIENTRYP PFNGLGETPERFCOUNTERINFOINTELPROC) (GLuint queryId, GLuint counterId, GLuint counterNameLength, GLchar *counterName, GLuint counterDescLength, GLchar *counterDesc, GLuint *counterOffset, GLuint *counterDataSize, GLuint *counterTypeEnum, GLuint *counterDataTypeEnum, GLuint64 *rawCounterMaxValue);
-typedef void (APIENTRYP PFNGLGETPERFQUERYDATAINTELPROC) (GLuint queryHandle, GLuint flags, GLsizei dataSize, GLvoid *data, GLuint *bytesWritten);
+typedef void (APIENTRYP PFNGLGETPERFQUERYDATAINTELPROC) (GLuint queryHandle, GLuint flags, GLsizei dataSize, void *data, GLuint *bytesWritten);
typedef void (APIENTRYP PFNGLGETPERFQUERYIDBYNAMEINTELPROC) (GLchar *queryName, GLuint *queryId);
typedef void (APIENTRYP PFNGLGETPERFQUERYINFOINTELPROC) (GLuint queryId, GLuint queryNameLength, GLchar *queryName, GLuint *dataSize, GLuint *noCounters, GLuint *noInstances, GLuint *capsMask);
#ifdef GL_GLEXT_PROTOTYPES
@@ -8972,7 +9270,7 @@ GLAPI void APIENTRY glEndPerfQueryINTEL (GLuint queryHandle);
GLAPI void APIENTRY glGetFirstPerfQueryIdINTEL (GLuint *queryId);
GLAPI void APIENTRY glGetNextPerfQueryIdINTEL (GLuint queryId, GLuint *nextQueryId);
GLAPI void APIENTRY glGetPerfCounterInfoINTEL (GLuint queryId, GLuint counterId, GLuint counterNameLength, GLchar *counterName, GLuint counterDescLength, GLchar *counterDesc, GLuint *counterOffset, GLuint *counterDataSize, GLuint *counterTypeEnum, GLuint *counterDataTypeEnum, GLuint64 *rawCounterMaxValue);
-GLAPI void APIENTRY glGetPerfQueryDataINTEL (GLuint queryHandle, GLuint flags, GLsizei dataSize, GLvoid *data, GLuint *bytesWritten);
+GLAPI void APIENTRY glGetPerfQueryDataINTEL (GLuint queryHandle, GLuint flags, GLsizei dataSize, void *data, GLuint *bytesWritten);
GLAPI void APIENTRY glGetPerfQueryIdByNameINTEL (GLchar *queryName, GLuint *queryId);
GLAPI void APIENTRY glGetPerfQueryInfoINTEL (GLuint queryId, GLuint queryNameLength, GLchar *queryName, GLuint *dataSize, GLuint *noCounters, GLuint *noInstances, GLuint *capsMask);
#endif
@@ -8993,6 +9291,11 @@ GLAPI void APIENTRY glGetPerfQueryInfoINTEL (GLuint queryId, GLuint queryNameLen
#define GL_PACK_INVERT_MESA 0x8758
#endif /* GL_MESA_pack_invert */
+#ifndef GL_MESA_program_binary_formats
+#define GL_MESA_program_binary_formats 1
+#define GL_PROGRAM_BINARY_FORMAT_MESA 0x875F
+#endif /* GL_MESA_program_binary_formats */
+
#ifndef GL_MESA_resize_buffers
#define GL_MESA_resize_buffers 1
typedef void (APIENTRYP PFNGLRESIZEBUFFERSMESAPROC) (void);
@@ -9005,6 +9308,13 @@ GLAPI void APIENTRY glResizeBuffersMESA (void);
#define GL_MESA_shader_integer_functions 1
#endif /* GL_MESA_shader_integer_functions */
+#ifndef GL_MESA_tile_raster_order
+#define GL_MESA_tile_raster_order 1
+#define GL_TILE_RASTER_ORDER_FIXED_MESA 0x8BB8
+#define GL_TILE_RASTER_ORDER_INCREASING_X_MESA 0x8BB9
+#define GL_TILE_RASTER_ORDER_INCREASING_Y_MESA 0x8BBA
+#endif /* GL_MESA_tile_raster_order */
+
#ifndef GL_MESA_window_pos
#define GL_MESA_window_pos 1
typedef void (APIENTRYP PFNGLWINDOWPOS2DMESAPROC) (GLdouble x, GLdouble y);
@@ -9231,6 +9541,10 @@ GLAPI void APIENTRY glBlendBarrierNV (void);
#define GL_BLEND_ADVANCED_COHERENT_NV 0x9285
#endif /* GL_NV_blend_equation_advanced_coherent */
+#ifndef GL_NV_blend_minmax_factor
+#define GL_NV_blend_minmax_factor 1
+#endif /* GL_NV_blend_minmax_factor */
+
#ifndef GL_NV_blend_square
#define GL_NV_blend_square 1
#endif /* GL_NV_blend_square */
@@ -9311,6 +9625,10 @@ GLAPI void APIENTRY glCallCommandListNV (GLuint list);
#define GL_COMPUTE_PROGRAM_PARAMETER_BUFFER_NV 0x90FC
#endif /* GL_NV_compute_program5 */
+#ifndef GL_NV_compute_shader_derivatives
+#define GL_NV_compute_shader_derivatives 1
+#endif /* GL_NV_compute_shader_derivatives */
+
#ifndef GL_NV_conditional_render
#define GL_NV_conditional_render 1
#define GL_QUERY_WAIT_NV 0x8E13
@@ -9348,6 +9666,11 @@ GLAPI void APIENTRY glConservativeRasterParameterfNV (GLenum pname, GLfloat valu
#endif
#endif /* GL_NV_conservative_raster_dilate */
+#ifndef GL_NV_conservative_raster_pre_snap
+#define GL_NV_conservative_raster_pre_snap 1
+#define GL_CONSERVATIVE_RASTER_MODE_PRE_SNAP_NV 0x9550
+#endif /* GL_NV_conservative_raster_pre_snap */
+
#ifndef GL_NV_conservative_raster_pre_snap_triangles
#define GL_NV_conservative_raster_pre_snap_triangles 1
#define GL_CONSERVATIVE_RASTER_MODE_NV 0x954D
@@ -9359,6 +9682,10 @@ GLAPI void APIENTRY glConservativeRasterParameteriNV (GLenum pname, GLint param)
#endif
#endif /* GL_NV_conservative_raster_pre_snap_triangles */
+#ifndef GL_NV_conservative_raster_underestimation
+#define GL_NV_conservative_raster_underestimation 1
+#endif /* GL_NV_conservative_raster_underestimation */
+
#ifndef GL_NV_copy_depth_to_color
#define GL_NV_copy_depth_to_color 1
#define GL_DEPTH_STENCIL_TO_RGBA_NV 0x886E
@@ -9600,6 +9927,10 @@ GLAPI void APIENTRY glGetProgramNamedParameterdvNV (GLuint id, GLsizei len, cons
#define GL_NV_fragment_program_option 1
#endif /* GL_NV_fragment_program_option */
+#ifndef GL_NV_fragment_shader_barycentric
+#define GL_NV_fragment_shader_barycentric 1
+#endif /* GL_NV_fragment_shader_barycentric */
+
#ifndef GL_NV_fragment_shader_interlock
#define GL_NV_fragment_shader_interlock 1
#endif /* GL_NV_fragment_shader_interlock */
@@ -9667,7 +9998,7 @@ GLAPI void APIENTRY glFramebufferTextureFaceEXT (GLenum target, GLenum attachmen
#define GL_PER_GPU_STORAGE_NV 0x9548
#define GL_MULTICAST_PROGRAMMABLE_SAMPLE_LOCATION_NV 0x9549
typedef void (APIENTRYP PFNGLRENDERGPUMASKNVPROC) (GLbitfield mask);
-typedef void (APIENTRYP PFNGLMULTICASTBUFFERSUBDATANVPROC) (GLbitfield gpuMask, GLuint buffer, GLintptr offset, GLsizeiptr size, const GLvoid *data);
+typedef void (APIENTRYP PFNGLMULTICASTBUFFERSUBDATANVPROC) (GLbitfield gpuMask, GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data);
typedef void (APIENTRYP PFNGLMULTICASTCOPYBUFFERSUBDATANVPROC) (GLuint readGpu, GLbitfield writeGpuMask, GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size);
typedef void (APIENTRYP PFNGLMULTICASTCOPYIMAGESUBDATANVPROC) (GLuint srcGpu, GLbitfield dstGpuMask, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth);
typedef void (APIENTRYP PFNGLMULTICASTBLITFRAMEBUFFERNVPROC) (GLuint srcGpu, GLuint dstGpu, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter);
@@ -9680,7 +10011,7 @@ typedef void (APIENTRYP PFNGLMULTICASTGETQUERYOBJECTI64VNVPROC) (GLuint gpu, GLu
typedef void (APIENTRYP PFNGLMULTICASTGETQUERYOBJECTUI64VNVPROC) (GLuint gpu, GLuint id, GLenum pname, GLuint64 *params);
#ifdef GL_GLEXT_PROTOTYPES
GLAPI void APIENTRY glRenderGpuMaskNV (GLbitfield mask);
-GLAPI void APIENTRY glMulticastBufferSubDataNV (GLbitfield gpuMask, GLuint buffer, GLintptr offset, GLsizeiptr size, const GLvoid *data);
+GLAPI void APIENTRY glMulticastBufferSubDataNV (GLbitfield gpuMask, GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data);
GLAPI void APIENTRY glMulticastCopyBufferSubDataNV (GLuint readGpu, GLbitfield writeGpuMask, GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size);
GLAPI void APIENTRY glMulticastCopyImageSubDataNV (GLuint srcGpu, GLbitfield dstGpuMask, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth);
GLAPI void APIENTRY glMulticastBlitFramebufferNV (GLuint srcGpu, GLuint dstGpu, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter);
@@ -9884,6 +10215,96 @@ GLAPI void APIENTRY glGetInternalformatSampleivNV (GLenum target, GLenum interna
#define GL_MAX_SPOT_EXPONENT_NV 0x8505
#endif /* GL_NV_light_max_exponent */
+#ifndef GL_NV_memory_attachment
+#define GL_NV_memory_attachment 1
+#define GL_ATTACHED_MEMORY_OBJECT_NV 0x95A4
+#define GL_ATTACHED_MEMORY_OFFSET_NV 0x95A5
+#define GL_MEMORY_ATTACHABLE_ALIGNMENT_NV 0x95A6
+#define GL_MEMORY_ATTACHABLE_SIZE_NV 0x95A7
+#define GL_MEMORY_ATTACHABLE_NV 0x95A8
+#define GL_DETACHED_MEMORY_INCARNATION_NV 0x95A9
+#define GL_DETACHED_TEXTURES_NV 0x95AA
+#define GL_DETACHED_BUFFERS_NV 0x95AB
+#define GL_MAX_DETACHED_TEXTURES_NV 0x95AC
+#define GL_MAX_DETACHED_BUFFERS_NV 0x95AD
+typedef void (APIENTRYP PFNGLGETMEMORYOBJECTDETACHEDRESOURCESUIVNVPROC) (GLuint memory, GLenum pname, GLint first, GLsizei count, GLuint *params);
+typedef void (APIENTRYP PFNGLRESETMEMORYOBJECTPARAMETERNVPROC) (GLuint memory, GLenum pname);
+typedef void (APIENTRYP PFNGLTEXATTACHMEMORYNVPROC) (GLenum target, GLuint memory, GLuint64 offset);
+typedef void (APIENTRYP PFNGLBUFFERATTACHMEMORYNVPROC) (GLenum target, GLuint memory, GLuint64 offset);
+typedef void (APIENTRYP PFNGLTEXTUREATTACHMEMORYNVPROC) (GLuint texture, GLuint memory, GLuint64 offset);
+typedef void (APIENTRYP PFNGLNAMEDBUFFERATTACHMEMORYNVPROC) (GLuint buffer, GLuint memory, GLuint64 offset);
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glGetMemoryObjectDetachedResourcesuivNV (GLuint memory, GLenum pname, GLint first, GLsizei count, GLuint *params);
+GLAPI void APIENTRY glResetMemoryObjectParameterNV (GLuint memory, GLenum pname);
+GLAPI void APIENTRY glTexAttachMemoryNV (GLenum target, GLuint memory, GLuint64 offset);
+GLAPI void APIENTRY glBufferAttachMemoryNV (GLenum target, GLuint memory, GLuint64 offset);
+GLAPI void APIENTRY glTextureAttachMemoryNV (GLuint texture, GLuint memory, GLuint64 offset);
+GLAPI void APIENTRY glNamedBufferAttachMemoryNV (GLuint buffer, GLuint memory, GLuint64 offset);
+#endif
+#endif /* GL_NV_memory_attachment */
+
+#ifndef GL_NV_mesh_shader
+#define GL_NV_mesh_shader 1
+#define GL_MESH_SHADER_NV 0x9559
+#define GL_TASK_SHADER_NV 0x955A
+#define GL_MAX_MESH_UNIFORM_BLOCKS_NV 0x8E60
+#define GL_MAX_MESH_TEXTURE_IMAGE_UNITS_NV 0x8E61
+#define GL_MAX_MESH_IMAGE_UNIFORMS_NV 0x8E62
+#define GL_MAX_MESH_UNIFORM_COMPONENTS_NV 0x8E63
+#define GL_MAX_MESH_ATOMIC_COUNTER_BUFFERS_NV 0x8E64
+#define GL_MAX_MESH_ATOMIC_COUNTERS_NV 0x8E65
+#define GL_MAX_MESH_SHADER_STORAGE_BLOCKS_NV 0x8E66
+#define GL_MAX_COMBINED_MESH_UNIFORM_COMPONENTS_NV 0x8E67
+#define GL_MAX_TASK_UNIFORM_BLOCKS_NV 0x8E68
+#define GL_MAX_TASK_TEXTURE_IMAGE_UNITS_NV 0x8E69
+#define GL_MAX_TASK_IMAGE_UNIFORMS_NV 0x8E6A
+#define GL_MAX_TASK_UNIFORM_COMPONENTS_NV 0x8E6B
+#define GL_MAX_TASK_ATOMIC_COUNTER_BUFFERS_NV 0x8E6C
+#define GL_MAX_TASK_ATOMIC_COUNTERS_NV 0x8E6D
+#define GL_MAX_TASK_SHADER_STORAGE_BLOCKS_NV 0x8E6E
+#define GL_MAX_COMBINED_TASK_UNIFORM_COMPONENTS_NV 0x8E6F
+#define GL_MAX_MESH_WORK_GROUP_INVOCATIONS_NV 0x95A2
+#define GL_MAX_TASK_WORK_GROUP_INVOCATIONS_NV 0x95A3
+#define GL_MAX_MESH_TOTAL_MEMORY_SIZE_NV 0x9536
+#define GL_MAX_TASK_TOTAL_MEMORY_SIZE_NV 0x9537
+#define GL_MAX_MESH_OUTPUT_VERTICES_NV 0x9538
+#define GL_MAX_MESH_OUTPUT_PRIMITIVES_NV 0x9539
+#define GL_MAX_TASK_OUTPUT_COUNT_NV 0x953A
+#define GL_MAX_DRAW_MESH_TASKS_COUNT_NV 0x953D
+#define GL_MAX_MESH_VIEWS_NV 0x9557
+#define GL_MESH_OUTPUT_PER_VERTEX_GRANULARITY_NV 0x92DF
+#define GL_MESH_OUTPUT_PER_PRIMITIVE_GRANULARITY_NV 0x9543
+#define GL_MAX_MESH_WORK_GROUP_SIZE_NV 0x953B
+#define GL_MAX_TASK_WORK_GROUP_SIZE_NV 0x953C
+#define GL_MESH_WORK_GROUP_SIZE_NV 0x953E
+#define GL_TASK_WORK_GROUP_SIZE_NV 0x953F
+#define GL_MESH_VERTICES_OUT_NV 0x9579
+#define GL_MESH_PRIMITIVES_OUT_NV 0x957A
+#define GL_MESH_OUTPUT_TYPE_NV 0x957B
+#define GL_UNIFORM_BLOCK_REFERENCED_BY_MESH_SHADER_NV 0x959C
+#define GL_UNIFORM_BLOCK_REFERENCED_BY_TASK_SHADER_NV 0x959D
+#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_MESH_SHADER_NV 0x959E
+#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TASK_SHADER_NV 0x959F
+#define GL_REFERENCED_BY_MESH_SHADER_NV 0x95A0
+#define GL_REFERENCED_BY_TASK_SHADER_NV 0x95A1
+#define GL_MESH_SUBROUTINE_NV 0x957C
+#define GL_TASK_SUBROUTINE_NV 0x957D
+#define GL_MESH_SUBROUTINE_UNIFORM_NV 0x957E
+#define GL_TASK_SUBROUTINE_UNIFORM_NV 0x957F
+#define GL_MESH_SHADER_BIT_NV 0x00000040
+#define GL_TASK_SHADER_BIT_NV 0x00000080
+typedef void (APIENTRYP PFNGLDRAWMESHTASKSNVPROC) (GLuint first, GLuint count);
+typedef void (APIENTRYP PFNGLDRAWMESHTASKSINDIRECTNVPROC) (GLintptr indirect);
+typedef void (APIENTRYP PFNGLMULTIDRAWMESHTASKSINDIRECTNVPROC) (GLintptr indirect, GLsizei drawcount, GLsizei stride);
+typedef void (APIENTRYP PFNGLMULTIDRAWMESHTASKSINDIRECTCOUNTNVPROC) (GLintptr indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride);
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glDrawMeshTasksNV (GLuint first, GLuint count);
+GLAPI void APIENTRY glDrawMeshTasksIndirectNV (GLintptr indirect);
+GLAPI void APIENTRY glMultiDrawMeshTasksIndirectNV (GLintptr indirect, GLsizei drawcount, GLsizei stride);
+GLAPI void APIENTRY glMultiDrawMeshTasksIndirectCountNV (GLintptr indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride);
+#endif
+#endif /* GL_NV_mesh_shader */
+
#ifndef GL_NV_multisample_coverage
#define GL_NV_multisample_coverage 1
#endif /* GL_NV_multisample_coverage */
@@ -10311,6 +10732,32 @@ GLAPI void APIENTRY glPrimitiveRestartIndexNV (GLuint index);
#endif
#endif /* GL_NV_primitive_restart */
+#ifndef GL_NV_query_resource
+#define GL_NV_query_resource 1
+#define GL_QUERY_RESOURCE_TYPE_VIDMEM_ALLOC_NV 0x9540
+#define GL_QUERY_RESOURCE_MEMTYPE_VIDMEM_NV 0x9542
+#define GL_QUERY_RESOURCE_SYS_RESERVED_NV 0x9544
+#define GL_QUERY_RESOURCE_TEXTURE_NV 0x9545
+#define GL_QUERY_RESOURCE_RENDERBUFFER_NV 0x9546
+#define GL_QUERY_RESOURCE_BUFFEROBJECT_NV 0x9547
+typedef GLint (APIENTRYP PFNGLQUERYRESOURCENVPROC) (GLenum queryType, GLint tagId, GLuint bufSize, GLint *buffer);
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI GLint APIENTRY glQueryResourceNV (GLenum queryType, GLint tagId, GLuint bufSize, GLint *buffer);
+#endif
+#endif /* GL_NV_query_resource */
+
+#ifndef GL_NV_query_resource_tag
+#define GL_NV_query_resource_tag 1
+typedef void (APIENTRYP PFNGLGENQUERYRESOURCETAGNVPROC) (GLsizei n, GLint *tagIds);
+typedef void (APIENTRYP PFNGLDELETEQUERYRESOURCETAGNVPROC) (GLsizei n, const GLint *tagIds);
+typedef void (APIENTRYP PFNGLQUERYRESOURCETAGNVPROC) (GLint tagId, const GLchar *tagString);
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glGenQueryResourceTagNV (GLsizei n, GLint *tagIds);
+GLAPI void APIENTRY glDeleteQueryResourceTagNV (GLsizei n, const GLint *tagIds);
+GLAPI void APIENTRY glQueryResourceTagNV (GLint tagId, const GLchar *tagString);
+#endif
+#endif /* GL_NV_query_resource_tag */
+
#ifndef GL_NV_register_combiners
#define GL_NV_register_combiners 1
#define GL_REGISTER_COMBINERS_NV 0x8522
@@ -10403,6 +10850,11 @@ GLAPI void APIENTRY glGetCombinerStageParameterfvNV (GLenum stage, GLenum pname,
#endif
#endif /* GL_NV_register_combiners2 */
+#ifndef GL_NV_representative_fragment_test
+#define GL_NV_representative_fragment_test 1
+#define GL_REPRESENTATIVE_FRAGMENT_TEST_NV 0x937F
+#endif /* GL_NV_representative_fragment_test */
+
#ifndef GL_NV_robustness_video_memory_purge
#define GL_NV_robustness_video_memory_purge 1
#define GL_PURGED_CONTEXT_RESET_NV 0x92BB
@@ -10432,6 +10884,18 @@ GLAPI void APIENTRY glResolveDepthValuesNV (void);
#define GL_NV_sample_mask_override_coverage 1
#endif /* GL_NV_sample_mask_override_coverage */
+#ifndef GL_NV_scissor_exclusive
+#define GL_NV_scissor_exclusive 1
+#define GL_SCISSOR_TEST_EXCLUSIVE_NV 0x9555
+#define GL_SCISSOR_BOX_EXCLUSIVE_NV 0x9556
+typedef void (APIENTRYP PFNGLSCISSOREXCLUSIVENVPROC) (GLint x, GLint y, GLsizei width, GLsizei height);
+typedef void (APIENTRYP PFNGLSCISSOREXCLUSIVEARRAYVNVPROC) (GLuint first, GLsizei count, const GLint *v);
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glScissorExclusiveNV (GLint x, GLint y, GLsizei width, GLsizei height);
+GLAPI void APIENTRY glScissorExclusiveArrayvNV (GLuint first, GLsizei count, const GLint *v);
+#endif
+#endif /* GL_NV_scissor_exclusive */
+
#ifndef GL_NV_shader_atomic_counters
#define GL_NV_shader_atomic_counters 1
#endif /* GL_NV_shader_atomic_counters */
@@ -10496,6 +10960,10 @@ GLAPI void APIENTRY glProgramUniformui64vNV (GLuint program, GLint location, GLs
#define GL_NV_shader_storage_buffer_object 1
#endif /* GL_NV_shader_storage_buffer_object */
+#ifndef GL_NV_shader_texture_footprint
+#define GL_NV_shader_texture_footprint 1
+#endif /* GL_NV_shader_texture_footprint */
+
#ifndef GL_NV_shader_thread_group
#define GL_NV_shader_thread_group 1
#define GL_WARP_SIZE_NV 0x9339
@@ -10507,6 +10975,47 @@ GLAPI void APIENTRY glProgramUniformui64vNV (GLuint program, GLint location, GLs
#define GL_NV_shader_thread_shuffle 1
#endif /* GL_NV_shader_thread_shuffle */
+#ifndef GL_NV_shading_rate_image
+#define GL_NV_shading_rate_image 1
+#define GL_SHADING_RATE_IMAGE_NV 0x9563
+#define GL_SHADING_RATE_NO_INVOCATIONS_NV 0x9564
+#define GL_SHADING_RATE_1_INVOCATION_PER_PIXEL_NV 0x9565
+#define GL_SHADING_RATE_1_INVOCATION_PER_1X2_PIXELS_NV 0x9566
+#define GL_SHADING_RATE_1_INVOCATION_PER_2X1_PIXELS_NV 0x9567
+#define GL_SHADING_RATE_1_INVOCATION_PER_2X2_PIXELS_NV 0x9568
+#define GL_SHADING_RATE_1_INVOCATION_PER_2X4_PIXELS_NV 0x9569
+#define GL_SHADING_RATE_1_INVOCATION_PER_4X2_PIXELS_NV 0x956A
+#define GL_SHADING_RATE_1_INVOCATION_PER_4X4_PIXELS_NV 0x956B
+#define GL_SHADING_RATE_2_INVOCATIONS_PER_PIXEL_NV 0x956C
+#define GL_SHADING_RATE_4_INVOCATIONS_PER_PIXEL_NV 0x956D
+#define GL_SHADING_RATE_8_INVOCATIONS_PER_PIXEL_NV 0x956E
+#define GL_SHADING_RATE_16_INVOCATIONS_PER_PIXEL_NV 0x956F
+#define GL_SHADING_RATE_IMAGE_BINDING_NV 0x955B
+#define GL_SHADING_RATE_IMAGE_TEXEL_WIDTH_NV 0x955C
+#define GL_SHADING_RATE_IMAGE_TEXEL_HEIGHT_NV 0x955D
+#define GL_SHADING_RATE_IMAGE_PALETTE_SIZE_NV 0x955E
+#define GL_MAX_COARSE_FRAGMENT_SAMPLES_NV 0x955F
+#define GL_SHADING_RATE_SAMPLE_ORDER_DEFAULT_NV 0x95AE
+#define GL_SHADING_RATE_SAMPLE_ORDER_PIXEL_MAJOR_NV 0x95AF
+#define GL_SHADING_RATE_SAMPLE_ORDER_SAMPLE_MAJOR_NV 0x95B0
+typedef void (APIENTRYP PFNGLBINDSHADINGRATEIMAGENVPROC) (GLuint texture);
+typedef void (APIENTRYP PFNGLGETSHADINGRATEIMAGEPALETTENVPROC) (GLuint viewport, GLuint entry, GLenum *rate);
+typedef void (APIENTRYP PFNGLGETSHADINGRATESAMPLELOCATIONIVNVPROC) (GLenum rate, GLuint samples, GLuint index, GLint *location);
+typedef void (APIENTRYP PFNGLSHADINGRATEIMAGEBARRIERNVPROC) (GLboolean synchronize);
+typedef void (APIENTRYP PFNGLSHADINGRATEIMAGEPALETTENVPROC) (GLuint viewport, GLuint first, GLsizei count, const GLenum *rates);
+typedef void (APIENTRYP PFNGLSHADINGRATESAMPLEORDERNVPROC) (GLenum order);
+typedef void (APIENTRYP PFNGLSHADINGRATESAMPLEORDERCUSTOMNVPROC) (GLenum rate, GLuint samples, const GLint *locations);
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI void APIENTRY glBindShadingRateImageNV (GLuint texture);
+GLAPI void APIENTRY glGetShadingRateImagePaletteNV (GLuint viewport, GLuint entry, GLenum *rate);
+GLAPI void APIENTRY glGetShadingRateSampleLocationivNV (GLenum rate, GLuint samples, GLuint index, GLint *location);
+GLAPI void APIENTRY glShadingRateImageBarrierNV (GLboolean synchronize);
+GLAPI void APIENTRY glShadingRateImagePaletteNV (GLuint viewport, GLuint first, GLsizei count, const GLenum *rates);
+GLAPI void APIENTRY glShadingRateSampleOrderNV (GLenum order);
+GLAPI void APIENTRY glShadingRateSampleOrderCustomNV (GLenum rate, GLuint samples, const GLint *locations);
+#endif
+#endif /* GL_NV_shading_rate_image */
+
#ifndef GL_NV_stereo_view_rendering
#define GL_NV_stereo_view_rendering 1
#endif /* GL_NV_stereo_view_rendering */
@@ -10587,6 +11096,10 @@ GLAPI void APIENTRY glTextureImage3DMultisampleCoverageNV (GLuint texture, GLenu
#define GL_MAX_RECTANGLE_TEXTURE_SIZE_NV 0x84F8
#endif /* GL_NV_texture_rectangle */
+#ifndef GL_NV_texture_rectangle_compressed
+#define GL_NV_texture_rectangle_compressed 1
+#endif /* GL_NV_texture_rectangle_compressed */
+
#ifndef GL_NV_texture_shader
#define GL_NV_texture_shader 1
#define GL_OFFSET_TEXTURE_RECTANGLE_NV 0x864C
@@ -10813,6 +11326,14 @@ GLAPI void APIENTRY glVDPAUUnmapSurfacesNV (GLsizei numSurface, const GLvdpauSur
#endif
#endif /* GL_NV_vdpau_interop */
+#ifndef GL_NV_vdpau_interop2
+#define GL_NV_vdpau_interop2 1
+typedef GLvdpauSurfaceNV (APIENTRYP PFNGLVDPAUREGISTERVIDEOSURFACEWITHPICTURESTRUCTURENVPROC) (const void *vdpSurface, GLenum target, GLsizei numTextureNames, const GLuint *textureNames, GLboolean isFrameStructure);
+#ifdef GL_GLEXT_PROTOTYPES
+GLAPI GLvdpauSurfaceNV APIENTRY glVDPAURegisterVideoSurfaceWithPictureStructureNV (const void *vdpSurface, GLenum target, GLsizei numTextureNames, const GLuint *textureNames, GLboolean isFrameStructure);
+#endif
+#endif /* GL_NV_vdpau_interop2 */
+
#ifndef GL_NV_vertex_array_range
#define GL_NV_vertex_array_range 1
#define GL_VERTEX_ARRAY_RANGE_NV 0x851D
diff --git a/src/gui/opengl/qopenglextensions_p.h b/src/gui/opengl/qopenglextensions_p.h
index af8ee8201d..137f1e831f 100644
--- a/src/gui/opengl/qopenglextensions_p.h
+++ b/src/gui/opengl/qopenglextensions_p.h
@@ -107,7 +107,7 @@ public:
QOpenGLExtensionsPrivate *d() const;
private:
- static bool isInitialized(const QOpenGLFunctionsPrivate *d) { return d != 0; }
+ static bool isInitialized(const QOpenGLFunctionsPrivate *d) { return d != nullptr; }
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QOpenGLExtensions::OpenGLExtensions)
diff --git a/src/gui/opengl/qopenglframebufferobject_p.h b/src/gui/opengl/qopenglframebufferobject_p.h
index 778a38b823..644bb6c59b 100644
--- a/src/gui/opengl/qopenglframebufferobject_p.h
+++ b/src/gui/opengl/qopenglframebufferobject_p.h
@@ -109,8 +109,8 @@ public:
class QOpenGLFramebufferObjectPrivate
{
public:
- QOpenGLFramebufferObjectPrivate() : fbo_guard(0), depth_buffer_guard(0)
- , stencil_buffer_guard(0)
+ QOpenGLFramebufferObjectPrivate() : fbo_guard(nullptr), depth_buffer_guard(nullptr)
+ , stencil_buffer_guard(nullptr)
, valid(false) {}
~QOpenGLFramebufferObjectPrivate() {}
@@ -135,9 +135,9 @@ public:
QOpenGLExtensions funcs;
struct ColorAttachment {
- ColorAttachment() : internalFormat(0), guard(0) { }
+ ColorAttachment() : internalFormat(0), guard(nullptr) { }
ColorAttachment(const QSize &size, GLenum internalFormat)
- : size(size), internalFormat(internalFormat), guard(0) { }
+ : size(size), internalFormat(internalFormat), guard(nullptr) { }
QSize size;
GLenum internalFormat;
QOpenGLSharedResourceGuard *guard;
diff --git a/src/gui/opengl/qopenglgradientcache_p.h b/src/gui/opengl/qopenglgradientcache_p.h
index cbdbbb0c54..1d34223bdd 100644
--- a/src/gui/opengl/qopenglgradientcache_p.h
+++ b/src/gui/opengl/qopenglgradientcache_p.h
@@ -66,7 +66,7 @@ class QOpenGL2GradientCache : public QOpenGLSharedResource
struct CacheInfo
{
inline CacheInfo(QGradientStops s, qreal op, QGradient::InterpolationMode mode) :
- stops(qMove(s)), opacity(op), interpolationMode(mode) {}
+ stops(std::move(s)), opacity(op), interpolationMode(mode) {}
GLuint texId;
QGradientStops stops;
diff --git a/src/gui/opengl/qopenglpaintengine_p.h b/src/gui/opengl/qopenglpaintengine_p.h
index 15ac240b89..81f17572b2 100644
--- a/src/gui/opengl/qopenglpaintengine_p.h
+++ b/src/gui/opengl/qopenglpaintengine_p.h
@@ -187,9 +187,9 @@ public:
QOpenGL2PaintEngineExPrivate(QOpenGL2PaintEngineEx *q_ptr) :
q(q_ptr),
- shaderManager(0),
+ shaderManager(nullptr),
width(0), height(0),
- ctx(0),
+ ctx(nullptr),
useSystemClip(true),
elementIndicesVBOId(0),
opacityArray(0),
@@ -361,9 +361,9 @@ void QOpenGL2PaintEngineExPrivate::uploadData(unsigned int arrayIndex, const GLf
opacityBuffer.allocate(data, count * sizeof(float));
}
if (arrayIndex == QT_OPACITY_ATTR)
- funcs.glVertexAttribPointer(arrayIndex, 1, GL_FLOAT, GL_FALSE, 0, 0);
+ funcs.glVertexAttribPointer(arrayIndex, 1, GL_FLOAT, GL_FALSE, 0, nullptr);
else
- funcs.glVertexAttribPointer(arrayIndex, 2, GL_FLOAT, GL_FALSE, 0, 0);
+ funcs.glVertexAttribPointer(arrayIndex, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
} else {
// If we already uploaded the data we don't have to do it again
if (data == vertexAttribPointers[arrayIndex])
diff --git a/src/gui/opengl/qopenglpixeltransferoptions.h b/src/gui/opengl/qopenglpixeltransferoptions.h
index 2fc52b82d8..195543ae90 100644
--- a/src/gui/opengl/qopenglpixeltransferoptions.h
+++ b/src/gui/opengl/qopenglpixeltransferoptions.h
@@ -55,14 +55,12 @@ class Q_GUI_EXPORT QOpenGLPixelTransferOptions
public:
QOpenGLPixelTransferOptions();
QOpenGLPixelTransferOptions(const QOpenGLPixelTransferOptions &);
-#ifdef Q_COMPILER_RVALUE_REFS
- QOpenGLPixelTransferOptions &operator=(QOpenGLPixelTransferOptions &&other) Q_DECL_NOTHROW
+ QOpenGLPixelTransferOptions &operator=(QOpenGLPixelTransferOptions &&other) noexcept
{ swap(other); return *this; }
-#endif
QOpenGLPixelTransferOptions &operator=(const QOpenGLPixelTransferOptions &);
~QOpenGLPixelTransferOptions();
- void swap(QOpenGLPixelTransferOptions &other) Q_DECL_NOTHROW
+ void swap(QOpenGLPixelTransferOptions &other) noexcept
{ data.swap(other.data); }
void setAlignment(int alignment);
diff --git a/src/gui/opengl/qopenglqueryhelper_p.h b/src/gui/opengl/qopenglqueryhelper_p.h
index 60dbf9c743..ad91ca9f96 100644
--- a/src/gui/opengl/qopenglqueryhelper_p.h
+++ b/src/gui/opengl/qopenglqueryhelper_p.h
@@ -65,18 +65,18 @@ class QOpenGLQueryHelper
{
public:
QOpenGLQueryHelper(QOpenGLContext *context)
- : GetQueryObjectuiv(0),
- GetQueryObjectiv(0),
- GetQueryiv(0),
- EndQuery(0),
- BeginQuery(0),
- IsQuery(0),
- DeleteQueries(0),
- GenQueries(0),
- GetInteger64v(0),
- GetQueryObjectui64v(0),
- GetQueryObjecti64v(0),
- QueryCounter(0)
+ : GetQueryObjectuiv(nullptr),
+ GetQueryObjectiv(nullptr),
+ GetQueryiv(nullptr),
+ EndQuery(nullptr),
+ BeginQuery(nullptr),
+ IsQuery(nullptr),
+ DeleteQueries(nullptr),
+ GenQueries(nullptr),
+ GetInteger64v(nullptr),
+ GetQueryObjectui64v(nullptr),
+ GetQueryObjecti64v(nullptr),
+ QueryCounter(nullptr)
{
Q_ASSERT(context);
diff --git a/src/gui/opengl/qopengltexture.cpp b/src/gui/opengl/qopengltexture.cpp
index e04a00e592..61a6202017 100644
--- a/src/gui/opengl/qopengltexture.cpp
+++ b/src/gui/opengl/qopengltexture.cpp
@@ -1467,6 +1467,122 @@ void QOpenGLTexturePrivate::setData(int mipLevel, int layer, int layerCount, QOp
}
}
+void QOpenGLTexturePrivate::setData(int xOffset, int yOffset, int zOffset, int width, int height, int depth,
+ int mipLevel, int layer, int layerCount, QOpenGLTexture::CubeMapFace cubeFace,
+ QOpenGLTexture::PixelFormat sourceFormat, QOpenGLTexture::PixelType sourceType,
+ const void *data, const QOpenGLPixelTransferOptions * const options)
+{
+ switch (target) {
+ case QOpenGLTexture::Target1D:
+ Q_UNUSED(layer);
+ Q_UNUSED(cubeFace);
+ Q_UNUSED(layerCount);
+ Q_UNUSED(yOffset);
+ Q_UNUSED(zOffset);
+ Q_UNUSED(height);
+ Q_UNUSED(depth);
+ texFuncs->glTextureSubImage1D(textureId, target, bindingTarget, mipLevel,
+ xOffset, width,
+ sourceFormat, sourceType, data, options);
+ break;
+
+ case QOpenGLTexture::Target1DArray:
+ Q_UNUSED(cubeFace);
+ Q_UNUSED(yOffset);
+ Q_UNUSED(zOffset);
+ Q_UNUSED(height);
+ Q_UNUSED(depth);
+ texFuncs->glTextureSubImage2D(textureId, target, bindingTarget, mipLevel,
+ xOffset, layer,
+ width,
+ layerCount,
+ sourceFormat, sourceType, data, options);
+ break;
+
+ case QOpenGLTexture::Target2D:
+ Q_UNUSED(layer);
+ Q_UNUSED(cubeFace);
+ Q_UNUSED(layerCount);
+ Q_UNUSED(zOffset);
+ Q_UNUSED(depth);
+ texFuncs->glTextureSubImage2D(textureId, target, bindingTarget, mipLevel,
+ xOffset, yOffset,
+ width, height,
+ sourceFormat, sourceType, data, options);
+ break;
+
+ case QOpenGLTexture::Target2DArray:
+ Q_UNUSED(cubeFace);
+ Q_UNUSED(zOffset);
+ Q_UNUSED(depth);
+ texFuncs->glTextureSubImage3D(textureId, target, bindingTarget, mipLevel,
+ xOffset, yOffset, layer,
+ width, height, layerCount,
+ sourceFormat, sourceType, data, options);
+ break;
+
+ case QOpenGLTexture::Target3D:
+ Q_UNUSED(cubeFace);
+ Q_UNUSED(layerCount);
+ texFuncs->glTextureSubImage3D(textureId, target, bindingTarget, mipLevel,
+ xOffset, yOffset, zOffset,
+ width, height, depth,
+ sourceFormat, sourceType, data, options);
+ break;
+
+ case QOpenGLTexture::TargetCubeMap:
+ Q_UNUSED(layer);
+ Q_UNUSED(layerCount);
+ Q_UNUSED(zOffset);
+ Q_UNUSED(depth);
+ texFuncs->glTextureSubImage2D(textureId, cubeFace, bindingTarget, mipLevel,
+ xOffset, yOffset,
+ width, height,
+ sourceFormat, sourceType, data, options);
+ break;
+
+ case QOpenGLTexture::TargetCubeMapArray: {
+ Q_UNUSED(zOffset);
+ Q_UNUSED(depth);
+ int faceIndex = cubeFace - QOpenGLTexture::CubeMapPositiveX;
+ int layerFace = 6 * layer + faceIndex;
+ texFuncs->glTextureSubImage3D(textureId, target, bindingTarget, mipLevel,
+ xOffset, yOffset, layerFace,
+ width, height,
+ layerCount,
+ sourceFormat, sourceType, data, options);
+ break;
+ }
+
+ case QOpenGLTexture::TargetRectangle:
+ Q_UNUSED(mipLevel);
+ Q_UNUSED(layer);
+ Q_UNUSED(cubeFace);
+ Q_UNUSED(layerCount);
+ Q_UNUSED(zOffset);
+ Q_UNUSED(depth);
+ texFuncs->glTextureSubImage2D(textureId, target, bindingTarget, 0,
+ xOffset, yOffset,
+ width, height,
+ sourceFormat, sourceType, data, options);
+ break;
+
+ case QOpenGLTexture::Target2DMultisample:
+ case QOpenGLTexture::Target2DMultisampleArray:
+ case QOpenGLTexture::TargetBuffer:
+ // We don't upload pixel data for these targets
+ qWarning("QOpenGLTexture::setData(): Texture target does not support pixel data upload");
+ break;
+ }
+
+ // If requested perform automatic mip map generation
+ if (mipLevel == 0 && autoGenerateMipMaps && mipLevels > 1) {
+ Q_Q(QOpenGLTexture);
+ q->generateMipMaps();
+ }
+}
+
+
void QOpenGLTexturePrivate::setCompressedData(int mipLevel, int layer, int layerCount,
QOpenGLTexture::CubeMapFace cubeFace,
int dataSize, const void *data,
@@ -3380,6 +3496,153 @@ void QOpenGLTexture::setData(PixelFormat sourceFormat, PixelType sourceType,
d->setData(0, 0, 1, QOpenGLTexture::CubeMapPositiveX, sourceFormat, sourceType, data, options);
}
+/*!
+ \since 5.14
+ \overload
+
+ This overload is to be used to update a part of the texture. Parameters \a
+ xOffset, \a yOffset, \a zOffset specify the texel offsets within the
+ texture. Parameters \a width, \a height and \a depth specify the dimensions
+ of the sub image.
+
+ The structure of the pixel data pointed to by \a data is specified by \a
+ sourceFormat and \a sourceType. The pixel data upload can optionally be
+ controlled by \a options.
+*/
+void QOpenGLTexture::setData(int xOffset, int yOffset, int zOffset,
+ int width, int height, int depth,
+ PixelFormat sourceFormat, PixelType sourceType,
+ const void *data, const QOpenGLPixelTransferOptions * const options)
+{
+ Q_D(QOpenGLTexture);
+ Q_ASSERT(d->textureId);
+ d->setData(xOffset, yOffset, zOffset,
+ width, height, depth,
+ 0, 0, 1,
+ QOpenGLTexture::CubeMapPositiveX, sourceFormat,
+ sourceType, data, options);
+}
+
+/*!
+ \since 5.14
+ \overload
+
+ This overload is to be used to update a part of the texture. Parameters \a
+ xOffset, \a yOffset, \a zOffset specify the texel offsets within the
+ texture. Parameters \a width, \a height and \a depth specify the dimensions
+ of the sub image. The mip map level the sub image we want to
+ update is specified with \a mipLevel.
+
+ The structure of the pixel data pointed to by \a data is specified by \a
+ sourceFormat and \a sourceType. The pixel data upload can optionally be
+ controlled by \a options.
+*/
+void QOpenGLTexture::setData(int xOffset, int yOffset, int zOffset,
+ int width, int height, int depth,
+ int mipLevel,
+ PixelFormat sourceFormat, PixelType sourceType,
+ const void *data, const QOpenGLPixelTransferOptions * const options)
+{
+ Q_D(QOpenGLTexture);
+ Q_ASSERT(d->textureId);
+ d->setData(xOffset, yOffset, zOffset,
+ width, height, depth,
+ mipLevel, 0, 1,
+ QOpenGLTexture::CubeMapPositiveX, sourceFormat,
+ sourceType, data, options);
+}
+
+/*!
+ \since 5.14
+ \overload
+
+ This overload is to be used to update a part of the texture. Parameters \a
+ xOffset, \a yOffset, \a zOffset specify the texel offsets within the
+ texture. Parameters \a width, \a height and \a depth specify the dimensions
+ of the sub image. The mip map level and layerof the sub image we want to
+ update are specified with \a mipLevel and \a layer.
+
+ The structure of the pixel data pointed to by \a data is specified by \a
+ sourceFormat and \a sourceType. The pixel data upload can optionally be
+ controlled by \a options.
+*/
+void QOpenGLTexture::setData(int xOffset, int yOffset, int zOffset,
+ int width, int height, int depth,
+ int mipLevel, int layer,
+ PixelFormat sourceFormat, PixelType sourceType,
+ const void *data, const QOpenGLPixelTransferOptions * const options)
+{
+ Q_D(QOpenGLTexture);
+ Q_ASSERT(d->textureId);
+ d->setData(xOffset, yOffset, zOffset,
+ width, height, depth,
+ mipLevel, layer, 1,
+ QOpenGLTexture::CubeMapPositiveX, sourceFormat,
+ sourceType, data, options);
+}
+
+/*!
+ \since 5.14
+ \overload
+
+ This overload is to be used to update a part of the texture. Parameters \a
+ xOffset, \a yOffset, \a zOffset specify the texel offsets within the
+ texture. Parameters \a width, \a height and \a depth specify the dimensions
+ of the sub image.The mip map level, layer and cube map face of the sub
+ image we want to update are specified with \a mipLevel, \a layer and \a
+ face.
+
+ The structure of the pixel data pointed to by \a data is specified by \a
+ sourceFormat and \a sourceType. The pixel data upload can optionally be
+ controlled by \a options.
+*/
+void QOpenGLTexture::setData(int xOffset, int yOffset, int zOffset,
+ int width, int height, int depth,
+ int mipLevel, int layer,
+ CubeMapFace face,
+ PixelFormat sourceFormat, PixelType sourceType,
+ const void *data, const QOpenGLPixelTransferOptions * const options)
+{
+ Q_D(QOpenGLTexture);
+ Q_ASSERT(d->textureId);
+ d->setData(xOffset, yOffset, zOffset,
+ width, height, depth,
+ mipLevel, layer, 1,
+ face, sourceFormat,
+ sourceType, data, options);
+}
+
+/*!
+ \since 5.14
+ \overload
+
+ This overload is to be used to update a part of the texture. Parameters \a
+ xOffset, \a yOffset, \a zOffset specify the texel offsets within the
+ texture. Parameters \a width, \a height and \a depth specify the dimensions
+ of the sub image.The mip map level, starting layer, cube map face and
+ number of layers of the sub image we want to update are specified with \a
+ mipLevel, \a layer, \a face and \a layerCount.
+
+ The structure of the pixel data pointed to by \a data is specified by \a
+ sourceFormat and \a sourceType. The pixel data upload can optionally be
+ controlled by \a options.
+*/
+void QOpenGLTexture::setData(int xOffset, int yOffset, int zOffset,
+ int width, int height, int depth,
+ int mipLevel, int layer,
+ CubeMapFace face, int layerCount,
+ PixelFormat sourceFormat, PixelType sourceType,
+ const void *data, const QOpenGLPixelTransferOptions * const options)
+{
+ Q_D(QOpenGLTexture);
+ Q_ASSERT(d->textureId);
+ d->setData(xOffset, yOffset, zOffset,
+ width, height, depth,
+ mipLevel, layer, layerCount,
+ face, sourceFormat,
+ sourceType, data, options);
+}
+
#if QT_DEPRECATED_SINCE(5, 3)
/*!
\obsolete
diff --git a/src/gui/opengl/qopengltexture.h b/src/gui/opengl/qopengltexture.h
index c0c5283374..7d984babc8 100644
--- a/src/gui/opengl/qopengltexture.h
+++ b/src/gui/opengl/qopengltexture.h
@@ -485,6 +485,32 @@ public:
void setData(PixelFormat sourceFormat, PixelType sourceType,
const void *data, const QOpenGLPixelTransferOptions * const options = nullptr);
+ void setData(int xOffset, int yOffset, int zOffset,
+ int width, int height, int depth,
+ PixelFormat sourceFormat, PixelType sourceType,
+ const void *data, const QOpenGLPixelTransferOptions * const options = nullptr);
+ void setData(int xOffset, int yOffset, int zOffset,
+ int width, int height, int depth, int mipLevel,
+ PixelFormat sourceFormat, PixelType sourceType,
+ const void *data, const QOpenGLPixelTransferOptions * const options = nullptr);
+ void setData(int xOffset, int yOffset, int zOffset,
+ int width, int height, int depth,
+ int mipLevel, int layer,
+ PixelFormat sourceFormat, PixelType sourceType,
+ const void *data, const QOpenGLPixelTransferOptions * const options = nullptr);
+ void setData(int xOffset, int yOffset, int zOffset,
+ int width, int height, int depth,
+ int mipLevel, int layer,
+ CubeMapFace cubeFace,
+ PixelFormat sourceFormat, PixelType sourceType,
+ const void *data, const QOpenGLPixelTransferOptions * const options = nullptr);
+ void setData(int xOffset, int yOffset, int zOffset,
+ int width, int height, int depth,
+ int mipLevel, int layer,
+ CubeMapFace cubeFace, int layerCount,
+ PixelFormat sourceFormat, PixelType sourceType,
+ const void *data, const QOpenGLPixelTransferOptions * const options = nullptr);
+
// Compressed data upload
// ### Qt 6: remove the non-const void * overloads
#if QT_DEPRECATED_SINCE(5, 3)
diff --git a/src/gui/opengl/qopengltexture_p.h b/src/gui/opengl/qopengltexture_p.h
index f7694f77bc..9f3457ad0a 100644
--- a/src/gui/opengl/qopengltexture_p.h
+++ b/src/gui/opengl/qopengltexture_p.h
@@ -101,6 +101,10 @@ public:
void setData(int mipLevel, int layer, int layerCount, QOpenGLTexture::CubeMapFace cubeFace,
QOpenGLTexture::PixelFormat sourceFormat, QOpenGLTexture::PixelType sourceType,
const void *data, const QOpenGLPixelTransferOptions * const options);
+ void setData(int xOffset, int yOffset, int zOffset, int width, int height, int depth,
+ int mipLevel, int layer, int layerCount, QOpenGLTexture::CubeMapFace cubeFace,
+ QOpenGLTexture::PixelFormat sourceFormat, QOpenGLTexture::PixelType sourceType,
+ const void *data, const QOpenGLPixelTransferOptions * const options);
void setCompressedData(int mipLevel, int layer, int layerCount, QOpenGLTexture::CubeMapFace cubeFace,
int dataSize, const void *data,
const QOpenGLPixelTransferOptions * const options);
diff --git a/src/gui/opengl/qopengltextureglyphcache_p.h b/src/gui/opengl/qopengltextureglyphcache_p.h
index 598cb00ee5..aed128cf9e 100644
--- a/src/gui/opengl/qopengltextureglyphcache_p.h
+++ b/src/gui/opengl/qopengltextureglyphcache_p.h
@@ -139,7 +139,7 @@ public:
inline void setPaintEnginePrivate(QOpenGL2PaintEngineExPrivate *p) { pex = p; }
- inline const QOpenGLContextGroup *contextGroup() const { return m_textureResource ? m_textureResource->group() : 0; }
+ inline const QOpenGLContextGroup *contextGroup() const { return m_textureResource ? m_textureResource->group() : nullptr; }
inline int serialNumber() const { return m_serialNumber; }
diff --git a/src/gui/opengl/qopengltexturehelper_p.h b/src/gui/opengl/qopengltexturehelper_p.h
index 00f6f9e5aa..62d0125daf 100644
--- a/src/gui/opengl/qopengltexturehelper_p.h
+++ b/src/gui/opengl/qopengltexturehelper_p.h
@@ -167,7 +167,7 @@ public:
inline void glTextureSubImage3D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, GLint xoffset, GLint yoffset, GLint zoffset,
GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type,
- const GLvoid *pixels, const QOpenGLPixelTransferOptions * const options = 0)
+ const GLvoid *pixels, const QOpenGLPixelTransferOptions * const options = nullptr)
{
if (options) {
QOpenGLPixelTransferOptions oldOptions = savePixelUploadOptions();
@@ -181,7 +181,7 @@ public:
inline void glTextureSubImage2D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, GLint xoffset, GLint yoffset,
GLsizei width, GLsizei height, GLenum format, GLenum type,
- const GLvoid *pixels, const QOpenGLPixelTransferOptions * const options = 0)
+ const GLvoid *pixels, const QOpenGLPixelTransferOptions * const options = nullptr)
{
if (options) {
QOpenGLPixelTransferOptions oldOptions = savePixelUploadOptions();
@@ -195,7 +195,7 @@ public:
inline void glTextureSubImage1D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level, GLint xoffset,
GLsizei width, GLenum format, GLenum type,
- const GLvoid *pixels, const QOpenGLPixelTransferOptions * const options = 0)
+ const GLvoid *pixels, const QOpenGLPixelTransferOptions * const options = nullptr)
{
if (options) {
QOpenGLPixelTransferOptions oldOptions = savePixelUploadOptions();
@@ -222,7 +222,7 @@ public:
inline void glCompressedTextureSubImage1D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level,
GLint xoffset, GLsizei width,
GLenum format, GLsizei imageSize, const GLvoid *bits,
- const QOpenGLPixelTransferOptions * const options = 0)
+ const QOpenGLPixelTransferOptions * const options = nullptr)
{
if (options) {
QOpenGLPixelTransferOptions oldOptions = savePixelUploadOptions();
@@ -238,7 +238,7 @@ public:
GLint xoffset, GLint yoffset,
GLsizei width, GLsizei height,
GLenum format, GLsizei imageSize, const GLvoid *bits,
- const QOpenGLPixelTransferOptions * const options = 0)
+ const QOpenGLPixelTransferOptions * const options = nullptr)
{
if (options) {
QOpenGLPixelTransferOptions oldOptions = savePixelUploadOptions();
@@ -254,7 +254,7 @@ public:
GLint xoffset, GLint yoffset, GLint zoffset,
GLsizei width, GLsizei height, GLsizei depth,
GLenum format, GLsizei imageSize, const GLvoid *bits,
- const QOpenGLPixelTransferOptions * const options = 0)
+ const QOpenGLPixelTransferOptions * const options = nullptr)
{
if (options) {
QOpenGLPixelTransferOptions oldOptions = savePixelUploadOptions();
@@ -269,7 +269,7 @@ public:
inline void glCompressedTextureImage1D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level,
GLenum internalFormat, GLsizei width,
GLint border, GLsizei imageSize, const GLvoid *bits,
- const QOpenGLPixelTransferOptions * const options = 0)
+ const QOpenGLPixelTransferOptions * const options = nullptr)
{
if (options) {
QOpenGLPixelTransferOptions oldOptions = savePixelUploadOptions();
@@ -284,7 +284,7 @@ public:
inline void glCompressedTextureImage2D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level,
GLenum internalFormat, GLsizei width, GLsizei height,
GLint border, GLsizei imageSize, const GLvoid *bits,
- const QOpenGLPixelTransferOptions * const options = 0)
+ const QOpenGLPixelTransferOptions * const options = nullptr)
{
if (options) {
@@ -300,7 +300,7 @@ public:
inline void glCompressedTextureImage3D(GLuint texture, GLenum target, GLenum bindingTarget, GLint level,
GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth,
GLint border, GLsizei imageSize, const GLvoid *bits,
- const QOpenGLPixelTransferOptions * const options = 0)
+ const QOpenGLPixelTransferOptions * const options = nullptr)
{
if (options) {
QOpenGLPixelTransferOptions oldOptions = savePixelUploadOptions();
diff --git a/src/gui/opengl/qopenglversionfunctions.h b/src/gui/opengl/qopenglversionfunctions.h
index f828e5668b..aa6e49b9ae 100644
--- a/src/gui/opengl/qopenglversionfunctions.h
+++ b/src/gui/opengl/qopenglversionfunctions.h
@@ -97,7 +97,7 @@ struct QOpenGLVersionStatus
OpenGLStatus status;
};
-inline uint qHash(const QOpenGLVersionStatus &v, uint seed = 0) Q_DECL_NOTHROW
+inline uint qHash(const QOpenGLVersionStatus &v, uint seed = 0) noexcept
{
return qHash(static_cast<int>(v.status * 1000)
+ v.version.first * 100 + v.version.second * 10, seed);
diff --git a/src/gui/painting/painting.pri b/src/gui/painting/painting.pri
index a90abed4c0..972cf387ff 100644
--- a/src/gui/painting/painting.pri
+++ b/src/gui/painting/painting.pri
@@ -8,7 +8,15 @@ HEADERS += \
painting/qbrush.h \
painting/qcolor.h \
painting/qcolor_p.h \
- painting/qcolorprofile_p.h \
+ painting/qcolormatrix_p.h \
+ painting/qcolorspace.h \
+ painting/qcolorspace_p.h \
+ painting/qcolortransferfunction_p.h \
+ painting/qcolortransfertable_p.h \
+ painting/qcolortransform.h \
+ painting/qcolortransform_p.h \
+ painting/qcolortrc_p.h \
+ painting/qcolortrclut_p.h \
painting/qcosmeticstroker_p.h \
painting/qdatabuffer_p.h \
painting/qdrawhelper_p.h \
@@ -17,6 +25,7 @@ HEADERS += \
painting/qemulationpaintengine_p.h \
painting/qfixed_p.h \
painting/qgrayraster_p.h \
+ painting/qicc_p.h \
painting/qmatrix.h \
painting/qmemrotate_p.h \
painting/qoutlinemapper_p.h \
@@ -64,12 +73,15 @@ SOURCES += \
painting/qblittable.cpp \
painting/qbrush.cpp \
painting/qcolor.cpp \
- painting/qcolorprofile.cpp \
+ painting/qcolorspace.cpp \
+ painting/qcolortransform.cpp \
+ painting/qcolortrclut.cpp \
painting/qcompositionfunctions.cpp \
painting/qcosmeticstroker.cpp \
painting/qdrawhelper.cpp \
painting/qemulationpaintengine.cpp \
painting/qgrayraster.c \
+ painting/qicc.cpp \
painting/qimagescale.cpp \
painting/qmatrix.cpp \
painting/qmemrotate.cpp \
diff --git a/src/gui/painting/qblendfunctions_p.h b/src/gui/painting/qblendfunctions_p.h
index 5ea78cdde2..080da98ec4 100644
--- a/src/gui/painting/qblendfunctions_p.h
+++ b/src/gui/painting/qblendfunctions_p.h
@@ -71,10 +71,10 @@ void qt_scale_image_16bit(uchar *destPixels, int dbpl,
const int ix = 0x00010000 * sx;
const int iy = 0x00010000 * sy;
-// qDebug() << "scale:" << endl
-// << " - target" << targetRect << endl
-// << " - source" << srcRect << endl
-// << " - clip" << clip << endl
+// qDebug() << "scale:" << Qt::endl
+// << " - target" << targetRect << Qt::endl
+// << " - source" << srcRect << Qt::endl
+// << " - clip" << clip << Qt::endl
// << " - sx=" << sx << " sy=" << sy << " ix=" << ix << " iy=" << iy;
QRect tr = targetRect.normalized().toRect();
@@ -162,10 +162,10 @@ template <typename T> void qt_scale_image_32bit(uchar *destPixels, int dbpl,
const int ix = 0x00010000 * sx;
const int iy = 0x00010000 * sy;
-// qDebug() << "scale:" << endl
-// << " - target" << targetRect << endl
-// << " - source" << srcRect << endl
-// << " - clip" << clip << endl
+// qDebug() << "scale:" << Qt::endl
+// << " - target" << targetRect << Qt::endl
+// << " - source" << srcRect << Qt::endl
+// << " - clip" << clip << Qt::endl
// << " - sx=" << sx << " sy=" << sy << " ix=" << ix << " iy=" << iy;
QRect tr = targetRect.normalized().toRect();
diff --git a/src/gui/painting/qbrush.cpp b/src/gui/painting/qbrush.cpp
index f56be55325..21e41979af 100644
--- a/src/gui/painting/qbrush.cpp
+++ b/src/gui/painting/qbrush.cpp
@@ -1140,11 +1140,11 @@ QDataStream &operator>>(QDataStream &s, QBrush &b)
if (s.version() >= QDataStream::Qt_5_5) {
QImage img;
s >> img;
- b.setTextureImage(qMove(img));
+ b.setTextureImage(std::move(img));
} else {
QPixmap pm;
s >> pm;
- b.setTexture(qMove(pm));
+ b.setTexture(std::move(pm));
}
} else if (style == Qt::LinearGradientPattern
|| style == Qt::RadialGradientPattern
@@ -1385,22 +1385,29 @@ QGradient::QGradient(Preset preset)
setCoordinateMode(ObjectMode);
setSpread(PadSpread);
- const QJsonValue start = presetData[QLatin1Literal("start")];
- const QJsonValue end = presetData[QLatin1Literal("end")];
- m_data.linear.x1 = start[QLatin1Literal("x")].toDouble();
- m_data.linear.y1 = start[QLatin1Literal("y")].toDouble();
- m_data.linear.x2 = end[QLatin1Literal("x")].toDouble();
- m_data.linear.y2 = end[QLatin1Literal("y")].toDouble();
+ const QJsonValue start = presetData[QLatin1String("start")];
+ const QJsonValue end = presetData[QLatin1String("end")];
+ m_data.linear.x1 = start[QLatin1String("x")].toDouble();
+ m_data.linear.y1 = start[QLatin1String("y")].toDouble();
+ m_data.linear.x2 = end[QLatin1String("x")].toDouble();
+ m_data.linear.y2 = end[QLatin1String("y")].toDouble();
for (const QJsonValue &stop : presetData[QLatin1String("stops")].toArray()) {
- setColorAt(stop[QLatin1Literal("position")].toDouble(),
- QColor(QRgb(stop[QLatin1Literal("color")].toInt())));
+ setColorAt(stop[QLatin1String("position")].toDouble(),
+ QColor(QRgb(stop[QLatin1String("color")].toInt())));
}
cachedPresets.insert(preset, *this);
}
}
+/*!
+ \internal
+*/
+QGradient::~QGradient()
+{
+}
+
QT_END_NAMESPACE
static void initGradientPresets() { Q_INIT_RESOURCE(qmake_webgradients); }
Q_CONSTRUCTOR_FUNCTION(initGradientPresets);
@@ -1785,6 +1792,12 @@ QLinearGradient::QLinearGradient(qreal xStart, qreal yStart, qreal xFinalStop, q
{
}
+/*!
+ \internal
+*/
+QLinearGradient::~QLinearGradient()
+{
+}
/*!
Returns the start point of this linear gradient in logical coordinates.
@@ -2056,6 +2069,13 @@ QRadialGradient::QRadialGradient(qreal cx, qreal cy, qreal centerRadius, qreal f
}
/*!
+ \internal
+*/
+QRadialGradient::~QRadialGradient()
+{
+}
+
+/*!
Returns the center of this radial gradient in logical coordinates.
\sa QGradient::stops()
@@ -2301,6 +2321,13 @@ QConicalGradient::QConicalGradient(qreal cx, qreal cy, qreal angle)
{
}
+/*!
+ \internal
+*/
+QConicalGradient::~QConicalGradient()
+{
+}
+
/*!
Constructs a conical with center at (0, 0) starting the
diff --git a/src/gui/painting/qbrush.h b/src/gui/painting/qbrush.h
index aee51c526d..ca51430cf4 100644
--- a/src/gui/painting/qbrush.h
+++ b/src/gui/painting/qbrush.h
@@ -79,11 +79,9 @@ public:
~QBrush();
QBrush &operator=(const QBrush &brush);
-#ifdef Q_COMPILER_RVALUE_REFS
- inline QBrush &operator=(QBrush &&other) Q_DECL_NOEXCEPT
+ inline QBrush &operator=(QBrush &&other) noexcept
{ qSwap(d, other.d); return *this; }
-#endif
- inline void swap(QBrush &other) Q_DECL_NOEXCEPT
+ inline void swap(QBrush &other) noexcept
{ qSwap(d, other.d); }
operator QVariant() const;
@@ -378,6 +376,7 @@ public:
QGradient();
QGradient(Preset);
+ ~QGradient();
Type type() const { return m_type; }
@@ -431,6 +430,7 @@ public:
QLinearGradient();
QLinearGradient(const QPointF &start, const QPointF &finalStop);
QLinearGradient(qreal xStart, qreal yStart, qreal xFinalStop, qreal yFinalStop);
+ ~QLinearGradient();
QPointF start() const;
void setStart(const QPointF &start);
@@ -455,6 +455,8 @@ public:
QRadialGradient(const QPointF &center, qreal centerRadius, const QPointF &focalPoint, qreal focalRadius);
QRadialGradient(qreal cx, qreal cy, qreal centerRadius, qreal fx, qreal fy, qreal focalRadius);
+ ~QRadialGradient();
+
QPointF center() const;
void setCenter(const QPointF &center);
inline void setCenter(qreal x, qreal y) { setCenter(QPointF(x, y)); }
@@ -480,6 +482,7 @@ public:
QConicalGradient();
QConicalGradient(const QPointF &center, qreal startAngle);
QConicalGradient(qreal cx, qreal cy, qreal startAngle);
+ ~QConicalGradient();
QPointF center() const;
void setCenter(const QPointF &center);
diff --git a/src/gui/painting/qcolor.cpp b/src/gui/painting/qcolor.cpp
index 07202f5b1c..6cbc30e79a 100644
--- a/src/gui/painting/qcolor.cpp
+++ b/src/gui/painting/qcolor.cpp
@@ -39,6 +39,7 @@
#include "qcolor.h"
#include "qcolor_p.h"
+#include "qfloat16.h"
#include "qnamespace.h"
#include "qdatastream.h"
#include "qvariant.h"
@@ -494,6 +495,14 @@ static QStringList get_colornames()
with this value, a pixel value will be used that is appropriate for the
underlying pixel format in use.
+ \section1 The Extended RGB Color Model
+
+ The extended RGB color model, also known as the scRGB color space,
+ is the same the RGB color model except it allows values under 0.0,
+ and over 1.0. This makes it possible to represent colors that would
+ otherwise be outside the range of the RGB colorspace but still use
+ the same values for colors inside the RGB colorspace.
+
\section1 The HSV Color Model
The RGB model is hardware-oriented. Its representation is close to
@@ -603,12 +612,13 @@ static QStringList get_colornames()
/*!
\enum QColor::Spec
- The type of color specified, either RGB, HSV, CMYK or HSL.
+ The type of color specified, either RGB, extended RGB, HSV, CMYK or HSL.
\value Rgb
\value Hsv
\value Cmyk
\value Hsl
+ \value ExtendedRgb
\value Invalid
\sa spec(), convertTo()
@@ -653,7 +663,7 @@ static QStringList get_colornames()
\sa isValid(), {QColor#Predefined Colors}{Predefined Colors}
*/
-QColor::QColor(Qt::GlobalColor color) Q_DECL_NOTHROW
+QColor::QColor(Qt::GlobalColor color) noexcept
{
#define QRGB(r, g, b) \
QRgb(((0xffu << 24) | ((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff)))
@@ -728,7 +738,7 @@ QColor::QColor(Qt::GlobalColor color) Q_DECL_NOTHROW
\sa fromRgb(), isValid()
*/
-QColor::QColor(QRgb color) Q_DECL_NOTHROW
+QColor::QColor(QRgb color) noexcept
{
cspec = Rgb;
ct.argb.alpha = 0xffff;
@@ -746,7 +756,7 @@ QColor::QColor(QRgb color) Q_DECL_NOTHROW
\sa fromRgba64()
*/
-QColor::QColor(QRgba64 rgba64) Q_DECL_NOTHROW
+QColor::QColor(QRgba64 rgba64) noexcept
{
setRgba64(rgba64);
}
@@ -760,7 +770,7 @@ QColor::QColor(QRgba64 rgba64) Q_DECL_NOTHROW
becomes a valid color by accident.
*/
-QColor::QColor(Spec spec) Q_DECL_NOTHROW
+QColor::QColor(Spec spec) noexcept
{
switch (spec) {
case Invalid:
@@ -778,6 +788,10 @@ QColor::QColor(Spec spec) Q_DECL_NOTHROW
case Hsl:
setHsl(0, 0, 0, 0);
break;
+ case ExtendedRgb:
+ cspec = spec;
+ setRgbF(0, 0, 0, 0);
+ break;
}
}
@@ -933,7 +947,7 @@ bool QColor::isValidColor(const QString &name)
\overload
\since 5.10
*/
-bool QColor::isValidColor(QStringView name) Q_DECL_NOTHROW
+bool QColor::isValidColor(QStringView name) noexcept
{
return name.size() && QColor().setColorFromString(name);
}
@@ -942,7 +956,7 @@ bool QColor::isValidColor(QStringView name) Q_DECL_NOTHROW
\overload
\since 5.8
*/
-bool QColor::isValidColor(QLatin1String name) Q_DECL_NOTHROW
+bool QColor::isValidColor(QLatin1String name) noexcept
{
return name.size() && QColor().setColorFromString(name);
}
@@ -1211,6 +1225,17 @@ void QColor::setHsl(int h, int s, int l, int a)
ct.ahsl.pad = 0;
}
+static inline qfloat16 &castF16(quint16 &v)
+{
+ // this works because qfloat16 internally is a quint16
+ return *reinterpret_cast<qfloat16 *>(&v);
+}
+
+static inline const qfloat16 &castF16(const quint16 &v)
+{
+ return *reinterpret_cast<const qfloat16 *>(&v);
+}
+
/*!
Sets the contents pointed to by \a r, \a g, \a b, and \a a, to the red,
green, blue, and alpha-channel (transparency) components of the color's
@@ -1226,18 +1251,27 @@ void QColor::getRgbF(qreal *r, qreal *g, qreal *b, qreal *a) const
if (!r || !g || !b)
return;
- if (cspec != Invalid && cspec != Rgb) {
+ if (cspec == Invalid)
+ return;
+
+ if (cspec != Rgb && cspec != ExtendedRgb) {
toRgb().getRgbF(r, g, b, a);
return;
}
- *r = ct.argb.red / qreal(USHRT_MAX);
- *g = ct.argb.green / qreal(USHRT_MAX);
- *b = ct.argb.blue / qreal(USHRT_MAX);
-
- if (a)
- *a = ct.argb.alpha / qreal(USHRT_MAX);
-
+ if (cspec == Rgb) {
+ *r = ct.argb.red / qreal(USHRT_MAX);
+ *g = ct.argb.green / qreal(USHRT_MAX);
+ *b = ct.argb.blue / qreal(USHRT_MAX);
+ if (a)
+ *a = ct.argb.alpha / qreal(USHRT_MAX);
+ } else {
+ *r = castF16(ct.argbExtended.redF16);
+ *g = castF16(ct.argbExtended.greenF16);
+ *b = castF16(ct.argbExtended.blueF16);
+ if (a)
+ *a = castF16(ct.argbExtended.alphaF16);
+ }
}
/*!
@@ -1274,26 +1308,35 @@ void QColor::getRgb(int *r, int *g, int *b, int *a) const
Sets the color channels of this color to \a r (red), \a g (green),
\a b (blue) and \a a (alpha, transparency).
- All values must be in the range 0.0-1.0.
+ The alpha value must be in the range 0.0-1.0.
+ If any of the other values are outside the range of 0.0-1.0 the
+ color model will be set as \c ExtendedRgb.
\sa rgb(), getRgbF(), setRgb()
*/
void QColor::setRgbF(qreal r, qreal g, qreal b, qreal a)
{
- if (r < qreal(0.0) || r > qreal(1.0)
- || g < qreal(0.0) || g > qreal(1.0)
- || b < qreal(0.0) || b > qreal(1.0)
- || a < qreal(0.0) || a > qreal(1.0)) {
- qWarning("QColor::setRgbF: RGB parameters out of range");
+ if (a < qreal(0.0) || a > qreal(1.0)) {
+ qWarning("QColor::setRgbF: Alpha parameter is out of range");
invalidate();
return;
}
-
+ if (r < qreal(0.0) || r > qreal(1.0) ||
+ g < qreal(0.0) || g > qreal(1.0) ||
+ b < qreal(0.0) || b > qreal(1.0) || cspec == ExtendedRgb) {
+ cspec = ExtendedRgb;
+ castF16(ct.argbExtended.redF16) = qfloat16(r);
+ castF16(ct.argbExtended.greenF16) = qfloat16(g);
+ castF16(ct.argbExtended.blueF16) = qfloat16(b);
+ castF16(ct.argbExtended.alphaF16) = qfloat16(a);
+ ct.argbExtended.pad = 0;
+ return;
+ }
cspec = Rgb;
- ct.argb.alpha = qRound(a * USHRT_MAX);
ct.argb.red = qRound(r * USHRT_MAX);
ct.argb.green = qRound(g * USHRT_MAX);
ct.argb.blue = qRound(b * USHRT_MAX);
+ ct.argb.alpha = qRound(a * USHRT_MAX);
ct.argb.pad = 0;
}
@@ -1306,7 +1349,7 @@ void QColor::setRgbF(qreal r, qreal g, qreal b, qreal a)
*/
void QColor::setRgb(int r, int g, int b, int a)
{
- if ((uint)r > 255 || (uint)g > 255 || (uint)b > 255 || (uint)a > 255) {
+ if (!isRgbaValid(r, g, b, a)) {
qWarning("QColor::setRgb: RGB parameters out of range");
invalidate();
return;
@@ -1330,7 +1373,7 @@ void QColor::setRgb(int r, int g, int b, int a)
\sa setRgba(), rgb(), rgba64()
*/
-QRgb QColor::rgba() const Q_DECL_NOTHROW
+QRgb QColor::rgba() const noexcept
{
if (cspec != Invalid && cspec != Rgb)
return toRgb().rgba();
@@ -1342,7 +1385,7 @@ QRgb QColor::rgba() const Q_DECL_NOTHROW
\sa rgba(), rgb(), setRgba64()
*/
-void QColor::setRgba(QRgb rgba) Q_DECL_NOTHROW
+void QColor::setRgba(QRgb rgba) noexcept
{
cspec = Rgb;
ct.argb.alpha = qAlpha(rgba) * 0x101;
@@ -1362,7 +1405,7 @@ void QColor::setRgba(QRgb rgba) Q_DECL_NOTHROW
\sa setRgba64(), rgba(), rgb()
*/
-QRgba64 QColor::rgba64() const Q_DECL_NOTHROW
+QRgba64 QColor::rgba64() const noexcept
{
if (cspec != Invalid && cspec != Rgb)
return toRgb().rgba64();
@@ -1376,7 +1419,7 @@ QRgba64 QColor::rgba64() const Q_DECL_NOTHROW
\sa setRgba(), rgba64()
*/
-void QColor::setRgba64(QRgba64 rgba) Q_DECL_NOTHROW
+void QColor::setRgba64(QRgba64 rgba) noexcept
{
cspec = Rgb;
ct.argb.alpha = rgba.alpha();
@@ -1393,7 +1436,7 @@ void QColor::setRgba64(QRgba64 rgba) Q_DECL_NOTHROW
\sa getRgb(), rgba()
*/
-QRgb QColor::rgb() const Q_DECL_NOTHROW
+QRgb QColor::rgb() const noexcept
{
if (cspec != Invalid && cspec != Rgb)
return toRgb().rgb();
@@ -1405,7 +1448,7 @@ QRgb QColor::rgb() const Q_DECL_NOTHROW
Sets the RGB value to \a rgb. The alpha value is set to opaque.
*/
-void QColor::setRgb(QRgb rgb) Q_DECL_NOTHROW
+void QColor::setRgb(QRgb rgb) noexcept
{
cspec = Rgb;
ct.argb.alpha = 0xffff;
@@ -1420,8 +1463,12 @@ void QColor::setRgb(QRgb rgb) Q_DECL_NOTHROW
\sa setAlpha(), alphaF(), {QColor#Alpha-Blended Drawing}{Alpha-Blended Drawing}
*/
-int QColor::alpha() const Q_DECL_NOTHROW
-{ return ct.argb.alpha >> 8; }
+int QColor::alpha() const noexcept
+{
+ if (cspec == ExtendedRgb)
+ return qRound(qreal(castF16(ct.argbExtended.alphaF16)) * 255);
+ return ct.argb.alpha >> 8;
+}
/*!
@@ -1434,6 +1481,11 @@ int QColor::alpha() const Q_DECL_NOTHROW
void QColor::setAlpha(int alpha)
{
QCOLOR_INT_RANGE_CHECK("QColor::setAlpha", alpha);
+ if (cspec == ExtendedRgb) {
+ constexpr qreal f = qreal(1.0) / 255;
+ castF16(ct.argbExtended.alphaF16) = alpha * f;
+ return;
+ }
ct.argb.alpha = alpha * 0x101;
}
@@ -1442,8 +1494,12 @@ void QColor::setAlpha(int alpha)
\sa setAlphaF(), alpha(), {QColor#Alpha-Blended Drawing}{Alpha-Blended Drawing}
*/
-qreal QColor::alphaF() const Q_DECL_NOTHROW
-{ return ct.argb.alpha / qreal(USHRT_MAX); }
+qreal QColor::alphaF() const noexcept
+{
+ if (cspec == ExtendedRgb)
+ return castF16(ct.argbExtended.alphaF16);
+ return ct.argb.alpha / qreal(USHRT_MAX);
+}
/*!
Sets the alpha of this color to \a alpha. qreal alpha is specified in the
@@ -1455,6 +1511,10 @@ qreal QColor::alphaF() const Q_DECL_NOTHROW
void QColor::setAlphaF(qreal alpha)
{
QCOLOR_REAL_RANGE_CHECK("QColor::setAlphaF", alpha);
+ if (cspec == ExtendedRgb) {
+ castF16(ct.argbExtended.alphaF16) = alpha;
+ return;
+ }
qreal tmp = alpha * USHRT_MAX;
ct.argb.alpha = qRound(tmp);
}
@@ -1465,7 +1525,7 @@ void QColor::setAlphaF(qreal alpha)
\sa setRed(), redF(), getRgb()
*/
-int QColor::red() const Q_DECL_NOTHROW
+int QColor::red() const noexcept
{
if (cspec != Invalid && cspec != Rgb)
return toRgb().red();
@@ -1492,7 +1552,7 @@ void QColor::setRed(int red)
\sa setGreen(), greenF(), getRgb()
*/
-int QColor::green() const Q_DECL_NOTHROW
+int QColor::green() const noexcept
{
if (cspec != Invalid && cspec != Rgb)
return toRgb().green();
@@ -1520,7 +1580,7 @@ void QColor::setGreen(int green)
\sa setBlue(), blueF(), getRgb()
*/
-int QColor::blue() const Q_DECL_NOTHROW
+int QColor::blue() const noexcept
{
if (cspec != Invalid && cspec != Rgb)
return toRgb().blue();
@@ -1548,27 +1608,31 @@ void QColor::setBlue(int blue)
\sa setRedF(), red(), getRgbF()
*/
-qreal QColor::redF() const Q_DECL_NOTHROW
+qreal QColor::redF() const noexcept
{
- if (cspec != Invalid && cspec != Rgb)
- return toRgb().redF();
- return ct.argb.red / qreal(USHRT_MAX);
+ if (cspec == Rgb || cspec == Invalid)
+ return ct.argb.red / qreal(USHRT_MAX);
+ if (cspec == ExtendedRgb)
+ return castF16(ct.argbExtended.redF16);
+
+ return toRgb().redF();
}
/*!
- Sets the red color component of this color to \a red. Float components
- are specified in the range 0.0-1.0.
+ Sets the red color component of this color to \a red. If \a red lies outside
+ the 0.0-1.0 range, the color model will be changed to \c ExtendedRgb.
\sa redF(), red(), setRgbF()
*/
void QColor::setRedF(qreal red)
{
- QCOLOR_REAL_RANGE_CHECK("QColor::setRedF", red);
- if (cspec != Rgb)
- setRgbF(red, greenF(), blueF(), alphaF());
- else
+ if (cspec == Rgb && red >= qreal(0.0) && red <= qreal(1.0))
ct.argb.red = qRound(red * USHRT_MAX);
+ else if (cspec == ExtendedRgb)
+ castF16(ct.argbExtended.redF16) = red;
+ else
+ setRgbF(red, greenF(), blueF(), alphaF());
}
/*!
@@ -1576,27 +1640,31 @@ void QColor::setRedF(qreal red)
\sa setGreenF(), green(), getRgbF()
*/
-qreal QColor::greenF() const Q_DECL_NOTHROW
+qreal QColor::greenF() const noexcept
{
- if (cspec != Invalid && cspec != Rgb)
- return toRgb().greenF();
- return ct.argb.green / qreal(USHRT_MAX);
+ if (cspec == Rgb || cspec == Invalid)
+ return ct.argb.green / qreal(USHRT_MAX);
+ if (cspec == ExtendedRgb)
+ return castF16(ct.argbExtended.greenF16);
+
+ return toRgb().greenF();
}
/*!
- Sets the green color component of this color to \a green. Float components
- are specified in the range 0.0-1.0.
+ Sets the green color component of this color to \a green. If \a green lies outside
+ the 0.0-1.0 range, the color model will be changed to \c ExtendedRgb.
\sa greenF(), green(), setRgbF()
*/
void QColor::setGreenF(qreal green)
{
- QCOLOR_REAL_RANGE_CHECK("QColor::setGreenF", green);
- if (cspec != Rgb)
- setRgbF(redF(), green, blueF(), alphaF());
- else
+ if (cspec == Rgb && green >= qreal(0.0) && green <= qreal(1.0))
ct.argb.green = qRound(green * USHRT_MAX);
+ else if (cspec == ExtendedRgb)
+ castF16(ct.argbExtended.greenF16) = green;
+ else
+ setRgbF(redF(), green, blueF(), alphaF());
}
/*!
@@ -1604,26 +1672,29 @@ void QColor::setGreenF(qreal green)
\sa setBlueF(), blue(), getRgbF()
*/
-qreal QColor::blueF() const Q_DECL_NOTHROW
+qreal QColor::blueF() const noexcept
{
- if (cspec != Invalid && cspec != Rgb)
- return toRgb().blueF();
- return ct.argb.blue / qreal(USHRT_MAX);
+ if (cspec == Rgb || cspec == Invalid)
+ return ct.argb.blue / qreal(USHRT_MAX);
+ if (cspec == ExtendedRgb)
+ return castF16(ct.argbExtended.blueF16);
+
+ return toRgb().blueF();
}
/*!
- Sets the blue color component of this color to \a blue. Float components
- are specified in the range 0.0-1.0.
-
+ Sets the blue color component of this color to \a blue. If \a blue lies outside
+ the 0.0-1.0 range, the color model will be changed to \c ExtendedRgb.
\sa blueF(), blue(), setRgbF()
*/
void QColor::setBlueF(qreal blue)
{
- QCOLOR_REAL_RANGE_CHECK("QColor::setBlueF", blue);
- if (cspec != Rgb)
- setRgbF(redF(), greenF(), blue, alphaF());
- else
+ if (cspec == Rgb && blue >= qreal(0.0) && blue <= qreal(1.0))
ct.argb.blue = qRound(blue * USHRT_MAX);
+ else if (cspec == ExtendedRgb)
+ castF16(ct.argbExtended.blueF16) = blue;
+ else
+ setRgbF(redF(), greenF(), blue, alphaF());
}
/*!
@@ -1634,7 +1705,7 @@ void QColor::setBlueF(qreal blue)
\sa hsvHue(), hslHue(), hueF(), getHsv(), {QColor#The HSV Color Model}{The HSV Color Model}
*/
-int QColor::hue() const Q_DECL_NOTHROW
+int QColor::hue() const noexcept
{
return hsvHue();
}
@@ -1644,7 +1715,7 @@ int QColor::hue() const Q_DECL_NOTHROW
\sa hueF(), hslHue(), getHsv(), {QColor#The HSV Color Model}{The HSV Color Model}
*/
-int QColor::hsvHue() const Q_DECL_NOTHROW
+int QColor::hsvHue() const noexcept
{
if (cspec != Invalid && cspec != Hsv)
return toHsv().hue();
@@ -1660,7 +1731,7 @@ int QColor::hsvHue() const Q_DECL_NOTHROW
Model}
*/
-int QColor::saturation() const Q_DECL_NOTHROW
+int QColor::saturation() const noexcept
{
return hsvSaturation();
}
@@ -1670,7 +1741,7 @@ int QColor::saturation() const Q_DECL_NOTHROW
\sa saturationF(), hslSaturation(), getHsv(), {QColor#The HSV Color Model}{The HSV Color Model}
*/
-int QColor::hsvSaturation() const Q_DECL_NOTHROW
+int QColor::hsvSaturation() const noexcept
{
if (cspec != Invalid && cspec != Hsv)
return toHsv().saturation();
@@ -1682,7 +1753,7 @@ int QColor::hsvSaturation() const Q_DECL_NOTHROW
\sa valueF(), getHsv(), {QColor#The HSV Color Model}{The HSV Color Model}
*/
-int QColor::value() const Q_DECL_NOTHROW
+int QColor::value() const noexcept
{
if (cspec != Invalid && cspec != Hsv)
return toHsv().value();
@@ -1696,7 +1767,7 @@ int QColor::value() const Q_DECL_NOTHROW
\sa hsvHueF(), hslHueF(), hue(), getHsvF(), {QColor#The HSV Color Model}{The HSV Color Model}
*/
-qreal QColor::hueF() const Q_DECL_NOTHROW
+qreal QColor::hueF() const noexcept
{
return hsvHueF();
}
@@ -1707,7 +1778,7 @@ qreal QColor::hueF() const Q_DECL_NOTHROW
\sa hue(), hslHueF(), getHsvF(), {QColor#The HSV Color Model}{The HSV Color
Model}
*/
-qreal QColor::hsvHueF() const Q_DECL_NOTHROW
+qreal QColor::hsvHueF() const noexcept
{
if (cspec != Invalid && cspec != Hsv)
return toHsv().hueF();
@@ -1722,7 +1793,7 @@ qreal QColor::hsvHueF() const Q_DECL_NOTHROW
\sa hsvSaturationF(), hslSaturationF(), saturation(), getHsvF(), {QColor#The HSV Color Model}{The HSV Color
Model}
*/
-qreal QColor::saturationF() const Q_DECL_NOTHROW
+qreal QColor::saturationF() const noexcept
{
return hsvSaturationF();
}
@@ -1732,7 +1803,7 @@ qreal QColor::saturationF() const Q_DECL_NOTHROW
\sa saturation(), hslSaturationF(), getHsvF(), {QColor#The HSV Color Model}{The HSV Color Model}
*/
-qreal QColor::hsvSaturationF() const Q_DECL_NOTHROW
+qreal QColor::hsvSaturationF() const noexcept
{
if (cspec != Invalid && cspec != Hsv)
return toHsv().saturationF();
@@ -1744,7 +1815,7 @@ qreal QColor::hsvSaturationF() const Q_DECL_NOTHROW
\sa value(), getHsvF(), {QColor#The HSV Color Model}{The HSV Color Model}
*/
-qreal QColor::valueF() const Q_DECL_NOTHROW
+qreal QColor::valueF() const noexcept
{
if (cspec != Invalid && cspec != Hsv)
return toHsv().valueF();
@@ -1758,7 +1829,7 @@ qreal QColor::valueF() const Q_DECL_NOTHROW
\sa hslHueF(), hsvHue(), getHsl(), {QColor#The HSL Color Model}{The HSL Color Model}
*/
-int QColor::hslHue() const Q_DECL_NOTHROW
+int QColor::hslHue() const noexcept
{
if (cspec != Invalid && cspec != Hsl)
return toHsl().hslHue();
@@ -1772,7 +1843,7 @@ int QColor::hslHue() const Q_DECL_NOTHROW
\sa hslSaturationF(), hsvSaturation(), getHsl(), {QColor#The HSL Color Model}{The HSL Color Model}
*/
-int QColor::hslSaturation() const Q_DECL_NOTHROW
+int QColor::hslSaturation() const noexcept
{
if (cspec != Invalid && cspec != Hsl)
return toHsl().hslSaturation();
@@ -1786,7 +1857,7 @@ int QColor::hslSaturation() const Q_DECL_NOTHROW
\sa lightnessF(), getHsl()
*/
-int QColor::lightness() const Q_DECL_NOTHROW
+int QColor::lightness() const noexcept
{
if (cspec != Invalid && cspec != Hsl)
return toHsl().lightness();
@@ -1800,7 +1871,7 @@ int QColor::lightness() const Q_DECL_NOTHROW
\sa hslHue(), hsvHueF(), getHslF()
*/
-qreal QColor::hslHueF() const Q_DECL_NOTHROW
+qreal QColor::hslHueF() const noexcept
{
if (cspec != Invalid && cspec != Hsl)
return toHsl().hslHueF();
@@ -1814,7 +1885,7 @@ qreal QColor::hslHueF() const Q_DECL_NOTHROW
\sa hslSaturation(), hsvSaturationF(), getHslF(), {QColor#The HSL Color Model}{The HSL Color Model}
*/
-qreal QColor::hslSaturationF() const Q_DECL_NOTHROW
+qreal QColor::hslSaturationF() const noexcept
{
if (cspec != Invalid && cspec != Hsl)
return toHsl().hslSaturationF();
@@ -1828,7 +1899,7 @@ qreal QColor::hslSaturationF() const Q_DECL_NOTHROW
\sa value(), getHslF()
*/
-qreal QColor::lightnessF() const Q_DECL_NOTHROW
+qreal QColor::lightnessF() const noexcept
{
if (cspec != Invalid && cspec != Hsl)
return toHsl().lightnessF();
@@ -1840,7 +1911,7 @@ qreal QColor::lightnessF() const Q_DECL_NOTHROW
\sa cyanF(), getCmyk(), {QColor#The CMYK Color Model}{The CMYK Color Model}
*/
-int QColor::cyan() const Q_DECL_NOTHROW
+int QColor::cyan() const noexcept
{
if (cspec != Invalid && cspec != Cmyk)
return toCmyk().cyan();
@@ -1852,7 +1923,7 @@ int QColor::cyan() const Q_DECL_NOTHROW
\sa magentaF(), getCmyk(), {QColor#The CMYK Color Model}{The CMYK Color Model}
*/
-int QColor::magenta() const Q_DECL_NOTHROW
+int QColor::magenta() const noexcept
{
if (cspec != Invalid && cspec != Cmyk)
return toCmyk().magenta();
@@ -1864,7 +1935,7 @@ int QColor::magenta() const Q_DECL_NOTHROW
\sa yellowF(), getCmyk(), {QColor#The CMYK Color Model}{The CMYK Color Model}
*/
-int QColor::yellow() const Q_DECL_NOTHROW
+int QColor::yellow() const noexcept
{
if (cspec != Invalid && cspec != Cmyk)
return toCmyk().yellow();
@@ -1877,7 +1948,7 @@ int QColor::yellow() const Q_DECL_NOTHROW
\sa blackF(), getCmyk(), {QColor#The CMYK Color Model}{The CMYK Color Model}
*/
-int QColor::black() const Q_DECL_NOTHROW
+int QColor::black() const noexcept
{
if (cspec != Invalid && cspec != Cmyk)
return toCmyk().black();
@@ -1889,7 +1960,7 @@ int QColor::black() const Q_DECL_NOTHROW
\sa cyan(), getCmykF(), {QColor#The CMYK Color Model}{The CMYK Color Model}
*/
-qreal QColor::cyanF() const Q_DECL_NOTHROW
+qreal QColor::cyanF() const noexcept
{
if (cspec != Invalid && cspec != Cmyk)
return toCmyk().cyanF();
@@ -1901,7 +1972,7 @@ qreal QColor::cyanF() const Q_DECL_NOTHROW
\sa magenta(), getCmykF(), {QColor#The CMYK Color Model}{The CMYK Color Model}
*/
-qreal QColor::magentaF() const Q_DECL_NOTHROW
+qreal QColor::magentaF() const noexcept
{
if (cspec != Invalid && cspec != Cmyk)
return toCmyk().magentaF();
@@ -1913,7 +1984,7 @@ qreal QColor::magentaF() const Q_DECL_NOTHROW
\sa yellow(), getCmykF(), {QColor#The CMYK Color Model}{The CMYK Color Model}
*/
-qreal QColor::yellowF() const Q_DECL_NOTHROW
+qreal QColor::yellowF() const noexcept
{
if (cspec != Invalid && cspec != Cmyk)
return toCmyk().yellowF();
@@ -1925,7 +1996,7 @@ qreal QColor::yellowF() const Q_DECL_NOTHROW
\sa black(), getCmykF(), {QColor#The CMYK Color Model}{The CMYK Color Model}
*/
-qreal QColor::blackF() const Q_DECL_NOTHROW
+qreal QColor::blackF() const noexcept
{
if (cspec != Invalid && cspec != Cmyk)
return toCmyk().blackF();
@@ -1933,18 +2004,43 @@ qreal QColor::blackF() const Q_DECL_NOTHROW
}
/*!
+ Create and returns an extended RGB QColor based on this color.
+ \since 5.14
+
+ \sa toRgb, convertTo()
+*/
+QColor QColor::toExtendedRgb() const noexcept
+{
+ if (!isValid() || cspec == ExtendedRgb)
+ return *this;
+ if (cspec != Rgb)
+ return toRgb().toExtendedRgb();
+
+ constexpr qreal f = qreal(1.0) / USHRT_MAX;
+ QColor color;
+ color.cspec = ExtendedRgb;
+ castF16(color.ct.argbExtended.alphaF16) = qfloat16(ct.argb.alpha * f);
+ castF16(color.ct.argbExtended.redF16) = qfloat16(ct.argb.red * f);
+ castF16(color.ct.argbExtended.greenF16) = qfloat16(ct.argb.green * f);
+ castF16(color.ct.argbExtended.blueF16) = qfloat16(ct.argb.blue * f);
+ color.ct.argbExtended.pad = 0;
+ return color;
+}
+
+/*!
Create and returns an RGB QColor based on this color.
\sa fromRgb(), convertTo(), isValid()
*/
-QColor QColor::toRgb() const Q_DECL_NOTHROW
+QColor QColor::toRgb() const noexcept
{
if (!isValid() || cspec == Rgb)
return *this;
QColor color;
color.cspec = Rgb;
- color.ct.argb.alpha = ct.argb.alpha;
+ if (cspec != ExtendedRgb)
+ color.ct.argb.alpha = ct.argb.alpha;
color.ct.argb.pad = 0;
switch (cspec) {
@@ -2066,6 +2162,12 @@ QColor QColor::toRgb() const Q_DECL_NOTHROW
color.ct.argb.blue = qRound((qreal(1.0) - (y * (qreal(1.0) - k) + k)) * USHRT_MAX);
break;
}
+ case ExtendedRgb:
+ color.ct.argb.alpha = qRound(USHRT_MAX * qreal(castF16(ct.argbExtended.alphaF16)));
+ color.ct.argb.red = qRound(USHRT_MAX * qBound(qreal(0.0), qreal(castF16(ct.argbExtended.redF16)), qreal(1.0)));
+ color.ct.argb.green = qRound(USHRT_MAX * qBound(qreal(0.0), qreal(castF16(ct.argbExtended.greenF16)), qreal(1.0)));
+ color.ct.argb.blue = qRound(USHRT_MAX * qBound(qreal(0.0), qreal(castF16(ct.argbExtended.blueF16)), qreal(1.0)));
+ break;
default:
break;
}
@@ -2083,7 +2185,7 @@ QColor QColor::toRgb() const Q_DECL_NOTHROW
\sa fromHsv(), convertTo(), isValid(), {QColor#The HSV Color Model}{The HSV Color Model}
*/
-QColor QColor::toHsv() const Q_DECL_NOTHROW
+QColor QColor::toHsv() const noexcept
{
if (!isValid() || cspec == Hsv)
return *this;
@@ -2134,7 +2236,7 @@ QColor QColor::toHsv() const Q_DECL_NOTHROW
\sa fromHsl(), convertTo(), isValid(), {QColor#The HSL Color Model}{The HSL Color Model}
*/
-QColor QColor::toHsl() const Q_DECL_NOTHROW
+QColor QColor::toHsl() const noexcept
{
if (!isValid() || cspec == Hsl)
return *this;
@@ -2190,7 +2292,7 @@ QColor QColor::toHsl() const Q_DECL_NOTHROW
\sa fromCmyk(), convertTo(), isValid(), {QColor#The CMYK Color Model}{The CMYK Color Model}
*/
-QColor QColor::toCmyk() const Q_DECL_NOTHROW
+QColor QColor::toCmyk() const noexcept
{
if (!isValid() || cspec == Cmyk)
return *this;
@@ -2231,13 +2333,15 @@ QColor QColor::toCmyk() const Q_DECL_NOTHROW
return color;
}
-QColor QColor::convertTo(QColor::Spec colorSpec) const Q_DECL_NOTHROW
+QColor QColor::convertTo(QColor::Spec colorSpec) const noexcept
{
if (colorSpec == cspec)
return *this;
switch (colorSpec) {
case Rgb:
return toRgb();
+ case ExtendedRgb:
+ return toExtendedRgb();
case Hsv:
return toHsv();
case Cmyk:
@@ -2262,7 +2366,7 @@ QColor QColor::convertTo(QColor::Spec colorSpec) const Q_DECL_NOTHROW
\sa fromRgba(), fromRgbF(), toRgb(), isValid()
*/
-QColor QColor::fromRgb(QRgb rgb) Q_DECL_NOTHROW
+QColor QColor::fromRgb(QRgb rgb) noexcept
{
return fromRgb(qRed(rgb), qGreen(rgb), qBlue(rgb));
}
@@ -2278,7 +2382,7 @@ QColor QColor::fromRgb(QRgb rgb) Q_DECL_NOTHROW
\sa fromRgb(), fromRgba64(), isValid()
*/
-QColor QColor::fromRgba(QRgb rgba) Q_DECL_NOTHROW
+QColor QColor::fromRgba(QRgb rgba) noexcept
{
return fromRgb(qRed(rgba), qGreen(rgba), qBlue(rgba), qAlpha(rgba));
}
@@ -2294,10 +2398,7 @@ QColor QColor::fromRgba(QRgb rgba) Q_DECL_NOTHROW
*/
QColor QColor::fromRgb(int r, int g, int b, int a)
{
- if (r < 0 || r > 255
- || g < 0 || g > 255
- || b < 0 || b > 255
- || a < 0 || a > 255) {
+ if (!isRgbaValid(r, g, b, a)) {
qWarning("QColor::fromRgb: RGB parameters out of range");
return QColor();
}
@@ -2317,20 +2418,32 @@ QColor QColor::fromRgb(int r, int g, int b, int a)
color values, \a r (red), \a g (green), \a b (blue), and \a a
(alpha-channel, i.e. transparency).
- All the values must be in the range 0.0-1.0.
+ The alpha value must be in the range 0.0-1.0.
+ If any of the other values are outside the range of 0.0-1.0 the
+ color model will be set as \c ExtendedRgb.
\sa fromRgb(), fromRgba64(), toRgb(), isValid()
*/
QColor QColor::fromRgbF(qreal r, qreal g, qreal b, qreal a)
{
- if (r < qreal(0.0) || r > qreal(1.0)
- || g < qreal(0.0) || g > qreal(1.0)
- || b < qreal(0.0) || b > qreal(1.0)
- || a < qreal(0.0) || a > qreal(1.0)) {
- qWarning("QColor::fromRgbF: RGB parameters out of range");
+ if (a < qreal(0.0) || a > qreal(1.0)) {
+ qWarning("QColor::fromRgbF: Alpha parameter out of range");
return QColor();
}
+ if (r < qreal(0.0) || r > qreal(1.0)
+ || g < qreal(0.0) || g > qreal(1.0)
+ || b < qreal(0.0) || b > qreal(1.0)) {
+ QColor color;
+ color.cspec = ExtendedRgb;
+ castF16(color.ct.argbExtended.alphaF16) = qfloat16(a);
+ castF16(color.ct.argbExtended.redF16) = qfloat16(r);
+ castF16(color.ct.argbExtended.greenF16) = qfloat16(g);
+ castF16(color.ct.argbExtended.blueF16) = qfloat16(b);
+ color.ct.argbExtended.pad = 0;
+ return color;
+ }
+
QColor color;
color.cspec = Rgb;
color.ct.argb.alpha = qRound(a * USHRT_MAX);
@@ -2351,7 +2464,7 @@ QColor QColor::fromRgbF(qreal r, qreal g, qreal b, qreal a)
\sa fromRgb(), fromRgbF(), toRgb(), isValid()
*/
-QColor QColor::fromRgba64(ushort r, ushort g, ushort b, ushort a) Q_DECL_NOTHROW
+QColor QColor::fromRgba64(ushort r, ushort g, ushort b, ushort a) noexcept
{
QColor color;
color.setRgba64(qRgba64(r, g, b, a));
@@ -2366,7 +2479,7 @@ QColor QColor::fromRgba64(ushort r, ushort g, ushort b, ushort a) Q_DECL_NOTHROW
\sa fromRgb(), fromRgbF(), toRgb(), isValid()
*/
-QColor QColor::fromRgba64(QRgba64 rgba64) Q_DECL_NOTHROW
+QColor QColor::fromRgba64(QRgba64 rgba64) noexcept
{
QColor color;
color.setRgba64(rgba64);
@@ -2714,7 +2827,7 @@ QColor QColor::fromCmykF(qreal c, qreal m, qreal y, qreal k, qreal a)
\sa darker(), isValid()
*/
-QColor QColor::lighter(int factor) const Q_DECL_NOTHROW
+QColor QColor::lighter(int factor) const noexcept
{
if (factor <= 0) // invalid lightness factor
return *this;
@@ -2759,7 +2872,7 @@ QColor QColor::lighter(int factor) const Q_DECL_NOTHROW
\sa lighter(), isValid()
*/
-QColor QColor::darker(int factor) const Q_DECL_NOTHROW
+QColor QColor::darker(int factor) const noexcept
{
if (factor <= 0) // invalid darkness factor
return *this;
@@ -2779,7 +2892,7 @@ QColor QColor::darker(int factor) const Q_DECL_NOTHROW
Use lighter(\a factor) instead.
*/
-QColor QColor::light(int factor) const Q_DECL_NOTHROW
+QColor QColor::light(int factor) const noexcept
{
return lighter(factor);
}
@@ -2789,7 +2902,7 @@ QColor QColor::light(int factor) const Q_DECL_NOTHROW
Use darker(\a factor) instead.
*/
-QColor QColor::dark(int factor) const Q_DECL_NOTHROW
+QColor QColor::dark(int factor) const noexcept
{
return darker(factor);
}
@@ -2799,7 +2912,7 @@ QColor QColor::dark(int factor) const Q_DECL_NOTHROW
/*!
Assigns a copy of \a color to this color, and returns a reference to it.
*/
-QColor &QColor::operator=(const QColor &color) Q_DECL_NOTHROW
+QColor &QColor::operator=(const QColor &color) noexcept
{
cspec = color.cspec;
ct.argb = color.ct.argb;
@@ -2810,7 +2923,7 @@ QColor &QColor::operator=(const QColor &color) Q_DECL_NOTHROW
/*! \overload
Assigns a copy of \a color and returns a reference to this color.
*/
-QColor &QColor::operator=(Qt::GlobalColor color) Q_DECL_NOTHROW
+QColor &QColor::operator=(Qt::GlobalColor color) noexcept
{
return operator=(QColor(color));
}
@@ -2819,7 +2932,7 @@ QColor &QColor::operator=(Qt::GlobalColor color) Q_DECL_NOTHROW
Returns \c true if this color has the same RGB and alpha values as \a color;
otherwise returns \c false.
*/
-bool QColor::operator==(const QColor &color) const Q_DECL_NOTHROW
+bool QColor::operator==(const QColor &color) const noexcept
{
if (cspec == Hsl && cspec == color.cspec) {
return (ct.argb.alpha == color.ct.argb.alpha
@@ -2846,7 +2959,7 @@ bool QColor::operator==(const QColor &color) const Q_DECL_NOTHROW
Returns \c true if this color has a different RGB and alpha values from
\a color; otherwise returns \c false.
*/
-bool QColor::operator!=(const QColor &color) const Q_DECL_NOTHROW
+bool QColor::operator!=(const QColor &color) const noexcept
{ return !operator==(color); }
@@ -2863,7 +2976,7 @@ QColor::operator QVariant() const
Marks the color as invalid and sets all components to zero (alpha is set
to fully opaque for compatibility with Qt 3).
*/
-void QColor::invalidate() Q_DECL_NOTHROW
+void QColor::invalidate() noexcept
{
cspec = Invalid;
ct.argb.alpha = USHRT_MAX;
@@ -2885,6 +2998,8 @@ QDebug operator<<(QDebug dbg, const QColor &c)
dbg.nospace() << "QColor(Invalid)";
else if (c.spec() == QColor::Rgb)
dbg.nospace() << "QColor(ARGB " << c.alphaF() << ", " << c.redF() << ", " << c.greenF() << ", " << c.blueF() << ')';
+ else if (c.spec() == QColor::ExtendedRgb)
+ dbg.nospace() << "QColor(Ext. ARGB " << c.alphaF() << ", " << c.redF() << ", " << c.greenF() << ", " << c.blueF() << ')';
else if (c.spec() == QColor::Hsv)
dbg.nospace() << "QColor(AHSV " << c.alphaF() << ", " << c.hueF() << ", " << c.saturationF() << ", " << c.valueF() << ')';
else if (c.spec() == QColor::Cmyk)
diff --git a/src/gui/painting/qcolor.h b/src/gui/painting/qcolor.h
index 4822612cf8..723b9fce73 100644
--- a/src/gui/painting/qcolor.h
+++ b/src/gui/painting/qcolor.h
@@ -64,35 +64,43 @@ Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QColor &);
class Q_GUI_EXPORT QColor
{
public:
- enum Spec { Invalid, Rgb, Hsv, Cmyk, Hsl };
+ enum Spec { Invalid, Rgb, Hsv, Cmyk, Hsl, ExtendedRgb };
enum NameFormat { HexRgb, HexArgb };
- inline QColor() Q_DECL_NOTHROW;
- QColor(Qt::GlobalColor color) Q_DECL_NOTHROW;
- inline QColor(int r, int g, int b, int a = 255);
- QColor(QRgb rgb) Q_DECL_NOTHROW;
- QColor(QRgba64 rgba64) Q_DECL_NOTHROW;
+ Q_DECL_CONSTEXPR QColor() noexcept
+ : cspec(Invalid), ct(USHRT_MAX, 0, 0, 0, 0) {}
+ QColor(Qt::GlobalColor color) noexcept;
+ Q_DECL_CONSTEXPR QColor(int r, int g, int b, int a = 255) noexcept
+ : cspec(isRgbaValid(r, g, b, a) ? Rgb : Invalid),
+ ct(cspec == Rgb ? a * 0x0101 : 0,
+ cspec == Rgb ? r * 0x0101 : 0,
+ cspec == Rgb ? g * 0x0101 : 0,
+ cspec == Rgb ? b * 0x0101 : 0,
+ 0) {}
+ QColor(QRgb rgb) noexcept;
+ QColor(QRgba64 rgba64) noexcept;
#if QT_STRINGVIEW_LEVEL < 2
inline QColor(const QString& name);
#endif
explicit inline QColor(QStringView name);
inline QColor(const char *aname) : QColor(QLatin1String(aname)) {}
inline QColor(QLatin1String name);
- QColor(Spec spec) Q_DECL_NOTHROW;
+ QColor(Spec spec) noexcept;
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
- inline QColor(const QColor &color) Q_DECL_NOTHROW; // ### Qt 6: remove all of these, the trivial ones are fine.
-# ifdef Q_COMPILER_RVALUE_REFS
- QColor(QColor &&other) Q_DECL_NOTHROW : cspec(other.cspec), ct(other.ct) {}
- QColor &operator=(QColor &&other) Q_DECL_NOTHROW
+ // ### Qt 6: remove all of these, the trivial ones are fine.
+ Q_DECL_CONSTEXPR QColor(const QColor &color) noexcept
+ : cspec(color.cspec), ct(color.ct)
+ {}
+ Q_DECL_CONSTEXPR QColor(QColor &&other) noexcept : cspec(other.cspec), ct(other.ct) {}
+ QColor &operator=(QColor &&other) noexcept
{ cspec = other.cspec; ct = other.ct; return *this; }
-# endif
- QColor &operator=(const QColor &) Q_DECL_NOTHROW;
+ QColor &operator=(const QColor &) noexcept;
#endif // Qt < 6
- QColor &operator=(Qt::GlobalColor color) Q_DECL_NOTHROW;
+ QColor &operator=(Qt::GlobalColor color) noexcept;
- bool isValid() const Q_DECL_NOTHROW;
+ bool isValid() const noexcept;
// ### Qt 6: merge overloads
QString name() const;
@@ -106,25 +114,25 @@ public:
static QStringList colorNames();
- inline Spec spec() const Q_DECL_NOTHROW
+ inline Spec spec() const noexcept
{ return cspec; }
- int alpha() const Q_DECL_NOTHROW;
+ int alpha() const noexcept;
void setAlpha(int alpha);
- qreal alphaF() const Q_DECL_NOTHROW;
+ qreal alphaF() const noexcept;
void setAlphaF(qreal alpha);
- int red() const Q_DECL_NOTHROW;
- int green() const Q_DECL_NOTHROW;
- int blue() const Q_DECL_NOTHROW;
+ int red() const noexcept;
+ int green() const noexcept;
+ int blue() const noexcept;
void setRed(int red);
void setGreen(int green);
void setBlue(int blue);
- qreal redF() const Q_DECL_NOTHROW;
- qreal greenF() const Q_DECL_NOTHROW;
- qreal blueF() const Q_DECL_NOTHROW;
+ qreal redF() const noexcept;
+ qreal greenF() const noexcept;
+ qreal blueF() const noexcept;
void setRedF(qreal red);
void setGreenF(qreal green);
void setBlueF(qreal blue);
@@ -135,26 +143,26 @@ public:
void getRgbF(qreal *r, qreal *g, qreal *b, qreal *a = nullptr) const;
void setRgbF(qreal r, qreal g, qreal b, qreal a = 1.0);
- QRgba64 rgba64() const Q_DECL_NOTHROW;
- void setRgba64(QRgba64 rgba) Q_DECL_NOTHROW;
+ QRgba64 rgba64() const noexcept;
+ void setRgba64(QRgba64 rgba) noexcept;
- QRgb rgba() const Q_DECL_NOTHROW;
- void setRgba(QRgb rgba) Q_DECL_NOTHROW;
+ QRgb rgba() const noexcept;
+ void setRgba(QRgb rgba) noexcept;
- QRgb rgb() const Q_DECL_NOTHROW;
- void setRgb(QRgb rgb) Q_DECL_NOTHROW;
+ QRgb rgb() const noexcept;
+ void setRgb(QRgb rgb) noexcept;
- int hue() const Q_DECL_NOTHROW; // 0 <= hue < 360
- int saturation() const Q_DECL_NOTHROW;
- int hsvHue() const Q_DECL_NOTHROW; // 0 <= hue < 360
- int hsvSaturation() const Q_DECL_NOTHROW;
- int value() const Q_DECL_NOTHROW;
+ int hue() const noexcept; // 0 <= hue < 360
+ int saturation() const noexcept;
+ int hsvHue() const noexcept; // 0 <= hue < 360
+ int hsvSaturation() const noexcept;
+ int value() const noexcept;
- qreal hueF() const Q_DECL_NOTHROW; // 0.0 <= hueF < 360.0
- qreal saturationF() const Q_DECL_NOTHROW;
- qreal hsvHueF() const Q_DECL_NOTHROW; // 0.0 <= hueF < 360.0
- qreal hsvSaturationF() const Q_DECL_NOTHROW;
- qreal valueF() const Q_DECL_NOTHROW;
+ qreal hueF() const noexcept; // 0.0 <= hueF < 360.0
+ qreal saturationF() const noexcept;
+ qreal hsvHueF() const noexcept; // 0.0 <= hueF < 360.0
+ qreal hsvSaturationF() const noexcept;
+ qreal valueF() const noexcept;
void getHsv(int *h, int *s, int *v, int *a = nullptr) const;
void setHsv(int h, int s, int v, int a = 255);
@@ -162,15 +170,15 @@ public:
void getHsvF(qreal *h, qreal *s, qreal *v, qreal *a = nullptr) const;
void setHsvF(qreal h, qreal s, qreal v, qreal a = 1.0);
- int cyan() const Q_DECL_NOTHROW;
- int magenta() const Q_DECL_NOTHROW;
- int yellow() const Q_DECL_NOTHROW;
- int black() const Q_DECL_NOTHROW;
+ int cyan() const noexcept;
+ int magenta() const noexcept;
+ int yellow() const noexcept;
+ int black() const noexcept;
- qreal cyanF() const Q_DECL_NOTHROW;
- qreal magentaF() const Q_DECL_NOTHROW;
- qreal yellowF() const Q_DECL_NOTHROW;
- qreal blackF() const Q_DECL_NOTHROW;
+ qreal cyanF() const noexcept;
+ qreal magentaF() const noexcept;
+ qreal yellowF() const noexcept;
+ qreal blackF() const noexcept;
void getCmyk(int *c, int *m, int *y, int *k, int *a = nullptr); // ### Qt 6: remove
void getCmyk(int *c, int *m, int *y, int *k, int *a = nullptr) const;
@@ -180,13 +188,13 @@ public:
void getCmykF(qreal *c, qreal *m, qreal *y, qreal *k, qreal *a = nullptr) const;
void setCmykF(qreal c, qreal m, qreal y, qreal k, qreal a = 1.0);
- int hslHue() const Q_DECL_NOTHROW; // 0 <= hue < 360
- int hslSaturation() const Q_DECL_NOTHROW;
- int lightness() const Q_DECL_NOTHROW;
+ int hslHue() const noexcept; // 0 <= hue < 360
+ int hslSaturation() const noexcept;
+ int lightness() const noexcept;
- qreal hslHueF() const Q_DECL_NOTHROW; // 0.0 <= hueF < 360.0
- qreal hslSaturationF() const Q_DECL_NOTHROW;
- qreal lightnessF() const Q_DECL_NOTHROW;
+ qreal hslHueF() const noexcept; // 0.0 <= hueF < 360.0
+ qreal hslSaturationF() const noexcept;
+ qreal lightnessF() const noexcept;
void getHsl(int *h, int *s, int *l, int *a = nullptr) const;
void setHsl(int h, int s, int l, int a = 255);
@@ -194,21 +202,22 @@ public:
void getHslF(qreal *h, qreal *s, qreal *l, qreal *a = nullptr) const;
void setHslF(qreal h, qreal s, qreal l, qreal a = 1.0);
- QColor toRgb() const Q_DECL_NOTHROW;
- QColor toHsv() const Q_DECL_NOTHROW;
- QColor toCmyk() const Q_DECL_NOTHROW;
- QColor toHsl() const Q_DECL_NOTHROW;
+ QColor toRgb() const noexcept;
+ QColor toHsv() const noexcept;
+ QColor toCmyk() const noexcept;
+ QColor toHsl() const noexcept;
+ QColor toExtendedRgb() const noexcept;
- Q_REQUIRED_RESULT QColor convertTo(Spec colorSpec) const Q_DECL_NOTHROW;
+ Q_REQUIRED_RESULT QColor convertTo(Spec colorSpec) const noexcept;
- static QColor fromRgb(QRgb rgb) Q_DECL_NOTHROW;
- static QColor fromRgba(QRgb rgba) Q_DECL_NOTHROW;
+ static QColor fromRgb(QRgb rgb) noexcept;
+ static QColor fromRgba(QRgb rgba) noexcept;
static QColor fromRgb(int r, int g, int b, int a = 255);
static QColor fromRgbF(qreal r, qreal g, qreal b, qreal a = 1.0);
- static QColor fromRgba64(ushort r, ushort g, ushort b, ushort a = USHRT_MAX) Q_DECL_NOTHROW;
- static QColor fromRgba64(QRgba64 rgba) Q_DECL_NOTHROW;
+ static QColor fromRgba64(ushort r, ushort g, ushort b, ushort a = USHRT_MAX) noexcept;
+ static QColor fromRgba64(QRgba64 rgba) noexcept;
static QColor fromHsv(int h, int s, int v, int a = 255);
static QColor fromHsvF(qreal h, qreal s, qreal v, qreal a = 1.0);
@@ -221,32 +230,42 @@ public:
#if QT_DEPRECATED_SINCE(5, 13)
QT_DEPRECATED_X("Use QColor::lighter() instead")
- Q_REQUIRED_RESULT QColor light(int f = 150) const Q_DECL_NOTHROW;
+ Q_REQUIRED_RESULT QColor light(int f = 150) const noexcept;
QT_DEPRECATED_X("Use QColor::darker() instead")
- Q_REQUIRED_RESULT QColor dark(int f = 200) const Q_DECL_NOTHROW;
+ Q_REQUIRED_RESULT QColor dark(int f = 200) const noexcept;
#endif
- Q_REQUIRED_RESULT QColor lighter(int f = 150) const Q_DECL_NOTHROW;
- Q_REQUIRED_RESULT QColor darker(int f = 200) const Q_DECL_NOTHROW;
+ Q_REQUIRED_RESULT QColor lighter(int f = 150) const noexcept;
+ Q_REQUIRED_RESULT QColor darker(int f = 200) const noexcept;
- bool operator==(const QColor &c) const Q_DECL_NOTHROW;
- bool operator!=(const QColor &c) const Q_DECL_NOTHROW;
+ bool operator==(const QColor &c) const noexcept;
+ bool operator!=(const QColor &c) const noexcept;
operator QVariant() const;
#if QT_STRINGVIEW_LEVEL < 2
static bool isValidColor(const QString &name);
#endif
- static bool isValidColor(QStringView) Q_DECL_NOTHROW;
- static bool isValidColor(QLatin1String) Q_DECL_NOTHROW;
+ static bool isValidColor(QStringView) noexcept;
+ static bool isValidColor(QLatin1String) noexcept;
private:
- void invalidate() Q_DECL_NOTHROW;
+ void invalidate() noexcept;
template <typename String>
bool setColorFromString(String name);
+ static Q_DECL_CONSTEXPR bool isRgbaValid(int r, int g, int b, int a = 255) noexcept Q_DECL_CONST_FUNCTION
+ {
+ return uint(r) <= 255 && uint(g) <= 255 && uint(b) <= 255 && uint(a) <= 255;
+ }
+
Spec cspec;
- union {
+ union CT {
+#ifdef Q_COMPILER_UNIFORM_INIT
+ CT() {} // doesn't init anything, thus can't be constexpr
+ Q_DECL_CONSTEXPR explicit CT(ushort a1, ushort a2, ushort a3, ushort a4, ushort a5) noexcept
+ : array{a1, a2, a3, a4, a5} {}
+#endif
struct {
ushort alpha;
ushort red;
@@ -275,6 +294,13 @@ private:
ushort lightness;
ushort pad;
} ahsl;
+ struct {
+ ushort alphaF16;
+ ushort redF16;
+ ushort greenF16;
+ ushort blueF16;
+ ushort pad;
+ } argbExtended;
ushort array[5];
} ct;
@@ -286,12 +312,6 @@ private:
};
Q_DECLARE_TYPEINFO(QColor, QT_VERSION >= QT_VERSION_CHECK(6,0,0) ? Q_MOVABLE_TYPE : Q_RELOCATABLE_TYPE);
-inline QColor::QColor() Q_DECL_NOTHROW
-{ invalidate(); }
-
-inline QColor::QColor(int r, int g, int b, int a)
-{ setRgb(r, g, b, a); }
-
inline QColor::QColor(QLatin1String aname)
{ setNamedColor(aname); }
@@ -303,13 +323,7 @@ inline QColor::QColor(const QString& aname)
{ setNamedColor(aname); }
#endif
-#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
-inline QColor::QColor(const QColor &acolor) Q_DECL_NOTHROW
- : cspec(acolor.cspec)
-{ ct.argb = acolor.ct.argb; }
-#endif
-
-inline bool QColor::isValid() const Q_DECL_NOTHROW
+inline bool QColor::isValid() const noexcept
{ return cspec != Invalid; }
QT_END_NAMESPACE
diff --git a/src/gui/painting/qcolormatrix_p.h b/src/gui/painting/qcolormatrix_p.h
new file mode 100644
index 0000000000..66db95df7e
--- /dev/null
+++ b/src/gui/painting/qcolormatrix_p.h
@@ -0,0 +1,249 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCOLORMATRIX_H
+#define QCOLORMATRIX_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtGui/qtguiglobal.h>
+#include <QtCore/qpoint.h>
+#include <cmath>
+
+QT_BEGIN_NAMESPACE
+
+// An abstract 3 value color
+class QColorVector
+{
+public:
+ QColorVector() = default;
+ Q_DECL_CONSTEXPR QColorVector(float x, float y, float z) : x(x), y(y), z(z), _unused(0.0f) { }
+ explicit Q_DECL_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())
+ , _unused(0.0f)
+ { }
+ float x; // X, x or red
+ float y; // Y, y or green
+ float z; // Z, Y or blue
+ float _unused;
+
+ friend inline bool operator==(const QColorVector &v1, const QColorVector &v2);
+ friend inline bool operator!=(const QColorVector &v1, const QColorVector &v2);
+ bool isNull() const
+ {
+ return !x && !y && !z;
+ }
+
+ static Q_DECL_CONSTEXPR QColorVector null() { return QColorVector(0.0f, 0.0f, 0.0f); }
+ static bool isValidChromaticity(const QPointF &chr)
+ {
+ if (chr.x() < qreal(0.0) || chr.x() > qreal(1.0))
+ return false;
+ if (chr.y() <= qreal(0.0) || chr.y() > qreal(1.0))
+ return false;
+ if (chr.x() + chr.y() > qreal(1.0))
+ return false;
+ return true;
+ }
+
+ // Common whitepoints:
+ static Q_DECL_CONSTEXPR QPointF D50Chromaticity() { return QPointF(0.34567, 0.35850); }
+ static Q_DECL_CONSTEXPR QPointF D65Chromaticity() { return QPointF(0.31271, 0.32902); }
+ static Q_DECL_CONSTEXPR QColorVector D50() { return QColorVector(D50Chromaticity()); }
+ static Q_DECL_CONSTEXPR QColorVector D65() { return QColorVector(D65Chromaticity()); }
+};
+
+inline bool operator==(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));
+}
+
+inline bool operator!=(const QColorVector &v1, const QColorVector &v2)
+{
+ return !(v1 == v2);
+}
+
+
+// A matrix mapping 3 value colors.
+// Not using QMatrix because only floats are needed and performance is critical.
+class QColorMatrix
+{
+public:
+ // We are storing the matrix transposed as that is more convenient:
+ QColorVector r;
+ 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
+ {
+ return r.isNull() && g.isNull() && b.isNull();
+ }
+ 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);
+ }
+
+ 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);
+ det = 1.0f / det;
+ QColorMatrix inv;
+ inv.r.x = (g.y * b.z - b.y * g.z) * det;
+ inv.r.y = (b.y * r.z - r.y * b.z) * det;
+ inv.r.z = (r.y * g.z - g.y * r.z) * det;
+ inv.g.x = (b.x * g.z - g.x * b.z) * det;
+ inv.g.y = (r.x * b.z - b.x * r.z) * det;
+ inv.g.z = (g.x * r.z - r.x * g.z) * det;
+ inv.b.x = (g.x * b.y - b.x * g.y) * det;
+ inv.b.y = (b.x * r.y - r.x * b.y) * det;
+ inv.b.z = (r.x * g.y - g.x * r.y) * det;
+ return inv;
+ }
+ QColorMatrix operator*(const QColorMatrix &o) const
+ {
+ 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.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.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;
+ return comb;
+
+ }
+ QColorVector map(const QColorVector &c) const
+ {
+ return QColorVector { c.x * r.x + c.y * g.x + c.z * b.x,
+ c.x * r.y + c.y * g.y + c.z * b.y,
+ c.x * r.z + c.y * g.z + c.z * b.z };
+ }
+ QColorMatrix transposed() const
+ {
+ return QColorMatrix { { r.x, g.x, b.x },
+ { r.y, g.y, b.y },
+ { r.z, g.z, b.z } };
+ }
+
+ static QColorMatrix null()
+ {
+ return { QColorVector::null(), QColorVector::null(), QColorVector::null() };
+ }
+ static QColorMatrix identity()
+ {
+ return { { 1.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, { 0.0f, 0.0f, 1.0f } };
+ }
+ static QColorMatrix fromScale(QColorVector v)
+ {
+ return QColorMatrix { { v.x, 0.0f, 0.0f },
+ { 0.0f, v.y, 0.0f },
+ { 0.0f, 0.0f, v.z } };
+ }
+ // These are used to recognize matrices from ICC profiles:
+ static QColorMatrix toXyzFromSRgb()
+ {
+ return QColorMatrix { { 0.4360217452f, 0.2224751115f, 0.0139281144f },
+ { 0.3851087987f, 0.7169067264f, 0.0971015394f },
+ { 0.1430812478f, 0.0606181994f, 0.7141585946f } };
+ }
+ static QColorMatrix toXyzFromAdobeRgb()
+ {
+ return QColorMatrix { { 0.6097189188f, 0.3111021519f, 0.0194766335f },
+ { 0.2052682191f, 0.6256770492f, 0.0608891509f },
+ { 0.1492247432f, 0.0632209629f, 0.7448224425f } };
+ }
+ static QColorMatrix toXyzFromDciP3D65()
+ {
+ return QColorMatrix { { 0.5150973201f, 0.2411795557f, -0.0010491034f },
+ { 0.2919696569f, 0.6922441125f, 0.0418830328f },
+ { 0.1571449190f, 0.0665764511f, 0.7843542695f } };
+ }
+ static QColorMatrix toXyzFromProPhotoRgb()
+ {
+ return QColorMatrix { { 0.7976672649f, 0.2880374491f, 0.0000000000f },
+ { 0.1351922452f, 0.7118769884f, 0.0000000000f },
+ { 0.0313525312f, 0.0000856627f, 0.8251883388f } };
+ }
+ static QColorMatrix toXyzFromBt2020()
+ {
+ return QColorMatrix { { 0.6506130099f, 0.2695676684f, -0.0018652577f },
+ { 0.1865101457f, 0.6840794086f, 0.0172256753f },
+ { 0.1270887405f, 0.0463530831f, 0.8098278046f } };
+ }
+};
+
+inline bool operator==(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
new file mode 100644
index 0000000000..8d3bbbe412
--- /dev/null
+++ b/src/gui/painting/qcolorspace.cpp
@@ -0,0 +1,744 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qcolorspace.h"
+#include "qcolorspace_p.h"
+
+#include "qcolortransform.h"
+#include "qcolormatrix_p.h"
+#include "qcolortransferfunction_p.h"
+#include "qcolortransform_p.h"
+#include "qicc_p.h"
+
+#include <qmath.h>
+#include <qtransform.h>
+
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+QBasicMutex QColorSpacePrivate::s_lutWriteLock;
+
+QColorSpacePrimaries::QColorSpacePrimaries(QColorSpace::Gamut gamut)
+{
+ switch (gamut) {
+ case QColorSpace::Gamut::SRgb:
+ redPoint = QPointF(0.640, 0.330);
+ greenPoint = QPointF(0.300, 0.600);
+ bluePoint = QPointF(0.150, 0.060);
+ whitePoint = QColorVector::D65Chromaticity();
+ break;
+ case QColorSpace::Gamut::DciP3D65:
+ redPoint = QPointF(0.680, 0.320);
+ greenPoint = QPointF(0.265, 0.690);
+ bluePoint = QPointF(0.150, 0.060);
+ whitePoint = QColorVector::D65Chromaticity();
+ break;
+ case QColorSpace::Gamut::Bt2020:
+ redPoint = QPointF(0.708, 0.292);
+ greenPoint = QPointF(0.190, 0.797);
+ bluePoint = QPointF(0.131, 0.046);
+ whitePoint = QColorVector::D65Chromaticity();
+ break;
+ case QColorSpace::Gamut::AdobeRgb:
+ redPoint = QPointF(0.640, 0.330);
+ greenPoint = QPointF(0.210, 0.710);
+ bluePoint = QPointF(0.150, 0.060);
+ whitePoint = QColorVector::D65Chromaticity();
+ break;
+ case QColorSpace::Gamut::ProPhotoRgb:
+ redPoint = QPointF(0.7347, 0.2653);
+ greenPoint = QPointF(0.1596, 0.8404);
+ bluePoint = QPointF(0.0366, 0.0001);
+ whitePoint = QColorVector::D50Chromaticity();
+ break;
+ default:
+ Q_UNREACHABLE();
+ }
+}
+
+bool QColorSpacePrimaries::areValid() const
+{
+ if (!QColorVector::isValidChromaticity(redPoint))
+ return false;
+ if (!QColorVector::isValidChromaticity(greenPoint))
+ return false;
+ if (!QColorVector::isValidChromaticity(bluePoint))
+ return false;
+ if (!QColorVector::isValidChromaticity(whitePoint))
+ return false;
+ return true;
+}
+
+QColorMatrix QColorSpacePrimaries::toXyzMatrix() const
+{
+ // This converts to XYZ in some undefined scale.
+ QColorMatrix toXyz = { QColorVector(redPoint),
+ QColorVector(greenPoint),
+ QColorVector(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);
+ 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);
+
+ 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;
+ }
+
+ return toXyz;
+}
+
+QColorSpacePrivate::QColorSpacePrivate()
+ : id(QColorSpace::Unknown)
+ , gamut(QColorSpace::Gamut::Custom)
+ , transferFunction(QColorSpace::TransferFunction::Custom)
+ , gamma(0.0f)
+ , whitePoint(QColorVector::null())
+ , toXyz(QColorMatrix::null())
+{
+}
+
+QColorSpacePrivate::QColorSpacePrivate(QColorSpace::ColorSpaceId colorSpaceId)
+ : id(colorSpaceId)
+{
+ switch (colorSpaceId) {
+ case QColorSpace::Undefined:
+ gamut = QColorSpace::Gamut::Custom;
+ transferFunction = QColorSpace::TransferFunction::Custom;
+ gamma = 0.0f;
+ description = QStringLiteral("Undefined");
+ break;
+ case QColorSpace::SRgb:
+ gamut = QColorSpace::Gamut::SRgb;
+ transferFunction = QColorSpace::TransferFunction::SRgb;
+ gamma = 2.31f; // ?
+ description = QStringLiteral("sRGB");
+ break;
+ case QColorSpace::SRgbLinear:
+ gamut = QColorSpace::Gamut::SRgb;
+ transferFunction = QColorSpace::TransferFunction::Linear;
+ gamma = 1.0f;
+ description = QStringLiteral("Linear sRGB");
+ break;
+ case QColorSpace::AdobeRgb:
+ gamut = QColorSpace::Gamut::AdobeRgb;
+ transferFunction = QColorSpace::TransferFunction::Gamma;
+ gamma = 2.19921875f; // Not quite 2.2, see https://www.adobe.com/digitalimag/pdfs/AdobeRGB1998.pdf
+ description = QStringLiteral("Adobe RGB");
+ break;
+ case QColorSpace::DisplayP3:
+ gamut = QColorSpace::Gamut::DciP3D65;
+ transferFunction = QColorSpace::TransferFunction::SRgb;
+ gamma = 2.31f; // ?
+ description = QStringLiteral("Display P3");
+ break;
+ case QColorSpace::ProPhotoRgb:
+ gamut = QColorSpace::Gamut::ProPhotoRgb;
+ transferFunction = QColorSpace::TransferFunction::ProPhotoRgb;
+ gamma = 1.8f;
+ description = QStringLiteral("ProPhoto RGB");
+ break;
+ case QColorSpace::Bt2020:
+ gamut = QColorSpace::Gamut::Bt2020;
+ transferFunction = QColorSpace::TransferFunction::Bt2020;
+ gamma = 2.1f; // ?
+ description = QStringLiteral("BT.2020");
+ break;
+ case QColorSpace::Unknown:
+ qWarning("Can not create an unknown color space");
+ Q_FALLTHROUGH();
+ default:
+ Q_UNREACHABLE();
+ }
+ initialize();
+}
+
+QColorSpacePrivate::QColorSpacePrivate(QColorSpace::Gamut gamut, QColorSpace::TransferFunction fun, float gamma)
+ : gamut(gamut)
+ , transferFunction(fun)
+ , gamma(gamma)
+{
+ if (!identifyColorSpace())
+ id = QColorSpace::Unknown;
+ initialize();
+}
+
+QColorSpacePrivate::QColorSpacePrivate(const QColorSpacePrimaries &primaries,
+ QColorSpace::TransferFunction fun,
+ float gamma)
+ : gamut(QColorSpace::Gamut::Custom)
+ , transferFunction(fun)
+ , gamma(gamma)
+{
+ Q_ASSERT(primaries.areValid());
+ toXyz = primaries.toXyzMatrix();
+ whitePoint = QColorVector(primaries.whitePoint);
+ if (!identifyColorSpace())
+ id = QColorSpace::Unknown;
+ setTransferFunction();
+}
+
+bool QColorSpacePrivate::identifyColorSpace()
+{
+ switch (gamut) {
+ case QColorSpace::Gamut::SRgb:
+ if (transferFunction == QColorSpace::TransferFunction::SRgb) {
+ id = QColorSpace::SRgb;
+ description = QStringLiteral("sRGB");
+ return true;
+ }
+ if (transferFunction == QColorSpace::TransferFunction::Linear) {
+ id = QColorSpace::SRgbLinear;
+ description = QStringLiteral("Linear sRGB");
+ return true;
+ }
+ break;
+ case QColorSpace::Gamut::AdobeRgb:
+ if (transferFunction == QColorSpace::TransferFunction::Gamma) {
+ if (qAbs(gamma - 2.19921875f) < (1/1024.0f)) {
+ id = QColorSpace::AdobeRgb;
+ description = QStringLiteral("Adobe RGB");
+ return true;
+ }
+ }
+ break;
+ case QColorSpace::Gamut::DciP3D65:
+ if (transferFunction == QColorSpace::TransferFunction::SRgb) {
+ id = QColorSpace::DisplayP3;
+ description = QStringLiteral("Display P3");
+ return true;
+ }
+ break;
+ case QColorSpace::Gamut::ProPhotoRgb:
+ if (transferFunction == QColorSpace::TransferFunction::ProPhotoRgb) {
+ id = QColorSpace::ProPhotoRgb;
+ description = QStringLiteral("ProPhoto RGB");
+ return true;
+ }
+ if (transferFunction == QColorSpace::TransferFunction::Gamma) {
+ // ProPhoto RGB's curve is effectively gamma 1.8 for 8bit precision.
+ if (qAbs(gamma - 1.8f) < (1/1024.0f)) {
+ id = QColorSpace::ProPhotoRgb;
+ description = QStringLiteral("ProPhoto RGB");
+ return true;
+ }
+ }
+ break;
+ case QColorSpace::Gamut::Bt2020:
+ if (transferFunction == QColorSpace::TransferFunction::Bt2020) {
+ id = QColorSpace::Bt2020;
+ description = QStringLiteral("BT.2020");
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+ return false;
+}
+
+void QColorSpacePrivate::initialize()
+{
+ setToXyzMatrix();
+ setTransferFunction();
+}
+
+void QColorSpacePrivate::setToXyzMatrix()
+{
+ if (gamut == QColorSpace::Gamut::Custom) {
+ toXyz = QColorMatrix::null();
+ whitePoint = QColorVector::D50();
+ return;
+ }
+ QColorSpacePrimaries primaries(gamut);
+ toXyz = primaries.toXyzMatrix();
+ whitePoint = QColorVector(primaries.whitePoint);
+}
+
+void QColorSpacePrivate::setTransferFunction()
+{
+ switch (transferFunction) {
+ case QColorSpace::TransferFunction::Linear:
+ trc[0].m_type = QColorTrc::Type::Function;
+ trc[0].m_fun = QColorTransferFunction();
+ break;
+ case QColorSpace::TransferFunction::Gamma:
+ trc[0].m_type = QColorTrc::Type::Function;
+ trc[0].m_fun = QColorTransferFunction::fromGamma(gamma);
+ break;
+ case QColorSpace::TransferFunction::SRgb:
+ trc[0].m_type = QColorTrc::Type::Function;
+ trc[0].m_fun = QColorTransferFunction::fromSRgb();
+ break;
+ case QColorSpace::TransferFunction::ProPhotoRgb:
+ trc[0].m_type = QColorTrc::Type::Function;
+ trc[0].m_fun = QColorTransferFunction::fromProPhotoRgb();
+ break;
+ case QColorSpace::TransferFunction::Bt2020:
+ trc[0].m_type = QColorTrc::Type::Function;
+ trc[0].m_fun = QColorTransferFunction::fromBt2020();
+ break;
+ case QColorSpace::TransferFunction::Custom:
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ trc[1] = trc[0];
+ trc[2] = trc[0];
+}
+
+QColorTransform QColorSpacePrivate::transformationToColorSpace(const QColorSpacePrivate *out) const
+{
+ Q_ASSERT(out);
+ QColorTransform combined;
+ combined.d_ptr.reset(new QColorTransformPrivate);
+ combined.d_ptr->colorSpaceIn = this;
+ combined.d_ptr->colorSpaceOut = out;
+ combined.d_ptr->colorMatrix = out->toXyz.inverted() * toXyz;
+ return combined;
+}
+
+/*!
+ \class QColorSpace
+ \brief The QColorSpace class provides a color space abstraction.
+ \since 5.14
+
+ \ingroup painting
+ \ingroup appearance
+ \inmodule QtGui
+
+ Color values can be interpreted in different ways, and based on the interpretation
+ can live in different spaces. We call this \e {color spaces}.
+
+ QColorSpace provides access to creating several predefined color spaces and
+ can generate QColorTransforms for converting colors from one color space to
+ another.
+
+ QColorSpace can also represent color spaces defined by ICC profiles or embedded
+ in images, that do not otherwise fit the predefined color spaces.
+
+ A color space can generally speaking be conceived as a combination of a transfer
+ function and a gamut. The gamut defines which colors the color space can represent.
+ A color space that can represent a wider range of colors is also known as a
+ wide-gamut color space. The gamut is 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 transfer function or gamma curve determines how each component in the
+ color space is encoded. These are used because human perception does not operate
+ linearly, and the transfer functions try to ensure that colors will seem evenly
+ spaced to human eyes.
+*/
+
+
+/*!
+ \enum QColorSpace::ColorSpaceId
+
+ Predefined color spaces.
+
+ \value Undefined An empty, invalid or unsupported color space.
+ \value Unknown A valid color space that doesn't match any of the predefined color spaces.
+ \value SRgb The sRGB color space, which Qt operates in by default. It is a close approximation
+ of how most classic monitors operate, and a mode most software and hardware support.
+ \l{http://www.color.org/chardata/rgb/srgb.xalter}{ICC registration of sRGB}.
+ \value SRgbLinear The sRGB color space with linear gamma. Useful for gamma-corrected blending.
+ \value AdobeRgb The Adobe RGB color space is a classic wide-gamut color space, using a gamma of 2.2.
+ \l{http://www.color.org/chardata/rgb/adobergb.xalter}{ICC registration of Adobe RGB (1998)}
+ \value DisplayP3 A color-space using the primaries of DCI-P3, but with the whitepoint and transfer
+ function of sRGB. Common in modern wide-gamut screens.
+ \l{http://www.color.org/chardata/rgb/DCIP3.xalter}{ICC registration of DCI-P3}
+ \value ProPhotoRgb The Pro Photo RGB color space, also known as ROMM RGB is a very wide gamut color space.
+ \l{http://www.color.org/chardata/rgb/rommrgb.xalter}{ICC registration of ROMM RGB}
+ \value Bt2020 BT.2020 also known as Rec.2020 is the color space of HDR TVs.
+ \l{http://www.color.org/chardata/rgb/BT2020.xalter}{ICC registration of BT.2020}
+*/
+
+/*!
+ \enum QColorSpace::Gamut
+
+ Predefined gamuts, or sets of primary colors.
+
+ \value Custom The gamut is undefined or does not match any predefined sets.
+ \value SRgb The sRGB gamut
+ \value AdobeRgb The Adobe RGB gamut
+ \value DciP3D65 The DCI-P3 gamut with the D65 whitepoint
+ \value ProPhotoRgb The ProPhoto RGB gamut with the D50 whitepoint
+ \value Bt2020 The BT.2020 gamut
+*/
+
+/*!
+ \enum QColorSpace::TransferFunction
+
+ Predefined transfer functions or gamma curves.
+
+ \value Custom The custom or null transfer function
+ \value Linear The linear transfer functions
+ \value Gamma A transfer function that is a real gamma curve based on the value of gamma()
+ \value SRgb The sRGB transfer function, composed of linear and gamma parts
+ \value ProPhotoRgb The ProPhoto RGB transfer function, composed of linear and gamma parts
+ \value Bt2020 The BT.2020 transfer function, composed of linear and gamma parts
+*/
+
+/*!
+ Creates a new colorspace object that represents \a colorSpaceId.
+ */
+QColorSpace::QColorSpace(QColorSpace::ColorSpaceId colorSpaceId)
+{
+ static QExplicitlySharedDataPointer<QColorSpacePrivate> predefinedColorspacePrivates[QColorSpace::Bt2020];
+ if (colorSpaceId <= QColorSpace::Unknown) {
+ if (!predefinedColorspacePrivates[0])
+ predefinedColorspacePrivates[0] = new QColorSpacePrivate(QColorSpace::Undefined);
+ d_ptr = predefinedColorspacePrivates[0]; // unknown and undefined both returns the static undefined colorspace.
+ } else {
+ if (!predefinedColorspacePrivates[colorSpaceId - 1])
+ predefinedColorspacePrivates[colorSpaceId - 1] = new QColorSpacePrivate(colorSpaceId);
+ d_ptr = predefinedColorspacePrivates[colorSpaceId - 1];
+ }
+
+ Q_ASSERT(colorSpaceId == QColorSpace::Undefined || isValid());
+}
+
+/*!
+ Creates a custom color space with the gamut \a gamut, using the transfer function \a fun and
+ optionally \a gamma.
+ */
+QColorSpace::QColorSpace(QColorSpace::Gamut gamut, QColorSpace::TransferFunction fun, float gamma)
+ : d_ptr(new QColorSpacePrivate(gamut, fun, gamma))
+{
+}
+
+/*!
+ Creates a custom color space with the gamut \a gamut, using a gamma transfer function of
+ \a gamma.
+ */
+QColorSpace::QColorSpace(QColorSpace::Gamut gamut, float gamma)
+ : d_ptr(new QColorSpacePrivate(gamut, TransferFunction::Gamma, gamma))
+{
+}
+
+/*!
+ Creates a custom colorspace with a gamut based on the chromaticities of the primary colors \a whitePoint,
+ \a redPoint, \a greenPoint and \a bluePoint, and using the transfer function \a fun and optionally \a gamma.
+ */
+QColorSpace::QColorSpace(const QPointF &whitePoint, const QPointF &redPoint,
+ const QPointF &greenPoint, const QPointF &bluePoint,
+ QColorSpace::TransferFunction fun, float gamma)
+{
+ QColorSpacePrimaries primaries(whitePoint, redPoint, greenPoint, bluePoint);
+ if (!primaries.areValid()) {
+ qWarning() << "QColorSpace attempted constructed from invalid primaries:" << whitePoint << redPoint << greenPoint << bluePoint;
+ d_ptr = QColorSpace(QColorSpace::Undefined).d_ptr;
+ return;
+ }
+ d_ptr = new QColorSpacePrivate(primaries, fun, gamma);
+}
+
+QColorSpace::~QColorSpace()
+{
+}
+
+QColorSpace::QColorSpace(QColorSpace &&colorSpace)
+ : d_ptr(std::move(colorSpace.d_ptr))
+{
+}
+
+QColorSpace::QColorSpace(const QColorSpace &colorSpace)
+ : d_ptr(colorSpace.d_ptr)
+{
+}
+
+QColorSpace &QColorSpace::operator=(QColorSpace &&colorSpace)
+{
+ d_ptr = std::move(colorSpace.d_ptr);
+ return *this;
+}
+
+QColorSpace &QColorSpace::operator=(const QColorSpace &colorSpace)
+{
+ d_ptr = colorSpace.d_ptr;
+ return *this;
+}
+
+/*!
+ Returns the id of the predefined color space this object
+ represents or \c Unknown if it doesn't match any of them.
+*/
+QColorSpace::ColorSpaceId QColorSpace::colorSpaceId() const noexcept
+{
+ return d_ptr->id;
+}
+
+/*!
+ Returns the predefined gamut of the color space
+ or \c Gamut::Custom if it doesn't match any of them.
+*/
+QColorSpace::Gamut QColorSpace::gamut() const noexcept
+{
+ return d_ptr->gamut;
+}
+
+/*!
+ Returns the predefined transfer function of the color space
+ or \c TransferFunction::Custom if it doesn't match any of them.
+
+ \sa gamma()
+*/
+QColorSpace::TransferFunction QColorSpace::transferFunction() const noexcept
+{
+ return d_ptr->transferFunction;
+}
+
+/*!
+ Returns the gamma value of color spaces with \c TransferFunction::Gamma,
+ an approximate gamma value for other predefined color spaces, or
+ 0.0 if no approximate gamma is known.
+
+ \sa transferFunction()
+*/
+float QColorSpace::gamma() const noexcept
+{
+ return d_ptr->gamma;
+}
+
+/*!
+ Returns an ICC profile representing the color space.
+
+ If the color space was generated from an ICC profile, that profile
+ is returned, otherwise one is generated.
+
+ \note Even invalid color spaces may return the ICC profile if they
+ were generated from one, to allow applications to implement wider
+ support themselves.
+
+ \sa fromIccProfile()
+*/
+QByteArray QColorSpace::iccProfile() const
+{
+ if (!d_ptr->iccProfile.isEmpty())
+ return d_ptr->iccProfile;
+ if (!isValid())
+ return QByteArray();
+ return QIcc::toIccProfile(*this);
+}
+
+/*!
+ 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.
+
+ If the ICC profile is not supported an invalid QColorSpace is returned
+ where you can still read the original ICC profile using iccProfile().
+
+ \sa iccProfile()
+*/
+QColorSpace QColorSpace::fromIccProfile(const QByteArray &iccProfile)
+{
+ QColorSpace colorSpace;
+ if (QIcc::fromIccProfile(iccProfile, &colorSpace))
+ return colorSpace;
+ colorSpace.d_ptr->id = QColorSpace::Undefined;
+ colorSpace.d_ptr->iccProfile = iccProfile;
+ return colorSpace;
+}
+
+/*!
+ Returns \c true if the color space is valid.
+*/
+bool QColorSpace::isValid() const noexcept
+{
+ return d_ptr->id != QColorSpace::Undefined && d_ptr->toXyz.isValid()
+ && d_ptr->trc[0].isValid() && d_ptr->trc[1].isValid() && d_ptr->trc[2].isValid();
+}
+
+/*!
+ \relates QColorSpace
+ Returns \c true if colorspace \a colorSpace1 is equal to colorspace \a colorSpace2;
+ otherwise returns \c false
+*/
+bool operator==(const QColorSpace &colorSpace1, const QColorSpace &colorSpace2)
+{
+ if (colorSpace1.d_ptr == colorSpace2.d_ptr)
+ return true;
+
+ if (colorSpace1.colorSpaceId() == QColorSpace::Undefined && colorSpace2.colorSpaceId() == QColorSpace::Undefined)
+ return colorSpace1.d_ptr->iccProfile == colorSpace2.d_ptr->iccProfile;
+
+ if (colorSpace1.colorSpaceId() != QColorSpace::Unknown && colorSpace2.colorSpaceId() != QColorSpace::Unknown)
+ return colorSpace1.colorSpaceId() == colorSpace2.colorSpaceId();
+
+ if (colorSpace1.gamut() != QColorSpace::Gamut::Custom && colorSpace2.gamut() != QColorSpace::Gamut::Custom) {
+ if (colorSpace1.gamut() != colorSpace2.gamut())
+ return false;
+ } else {
+ if (colorSpace1.d_ptr->toXyz != colorSpace2.d_ptr->toXyz)
+ return false;
+ }
+
+ if (colorSpace1.transferFunction() != QColorSpace::TransferFunction::Custom &&
+ colorSpace2.transferFunction() != QColorSpace::TransferFunction::Custom) {
+ if (colorSpace1.transferFunction() != colorSpace2.transferFunction())
+ return false;
+ if (colorSpace1.transferFunction() == QColorSpace::TransferFunction::Gamma)
+ return colorSpace1.gamma() == colorSpace2.gamma();
+ return true;
+ }
+
+ if (colorSpace1.d_ptr->trc[0] != colorSpace2.d_ptr->trc[0] ||
+ colorSpace1.d_ptr->trc[1] != colorSpace2.d_ptr->trc[1] ||
+ colorSpace1.d_ptr->trc[2] != colorSpace2.d_ptr->trc[2])
+ return false;
+
+ return true;
+}
+
+/*!
+ \fn bool operator!=(const QColorSpace &colorSpace1, const QColorSpace &colorSpace2)
+ \relates QColorSpace
+
+ Returns \c true if colorspace \a colorSpace1 is not equal to colorspace \a colorSpace2;
+ otherwise returns \c false
+*/
+
+/*!
+ Generates and returns a color space transformation from this color space to
+ \a colorspace.
+*/
+QColorTransform QColorSpace::transformationToColorSpace(const QColorSpace &colorspace) const
+{
+ if (!isValid() || !colorspace.isValid())
+ return QColorTransform();
+
+ return d_ptr->transformationToColorSpace(colorspace.d_ptr.constData());
+}
+
+/*!
+ \internal
+*/
+QColorSpacePrivate *QColorSpace::d_func()
+{
+ d_ptr.detach();
+ return d_ptr.data();
+}
+
+/*!
+ \fn const QColorSpacePrivate* QColorSpacePrivate::d_func() const
+ \internal
+*/
+
+/*****************************************************************************
+ QColorSpace stream functions
+ *****************************************************************************/
+#if !defined(QT_NO_DATASTREAM)
+/*!
+ \fn QDataStream &operator<<(QDataStream &stream, const QColorSpace &colorSpace)
+ \relates QColorSpace
+
+ Writes the given \a colorSpace to the given \a stream as an ICC profile.
+
+ \sa QColorSpace::iccProfile(), {Serializing Qt Data Types}
+*/
+
+QDataStream &operator<<(QDataStream &s, const QColorSpace &image)
+{
+ s << image.iccProfile();
+ return s;
+}
+
+/*!
+ \fn QDataStream &operator>>(QDataStream &stream, QColorSpace &colorSpace)
+ \relates QColorSpace
+
+ Reads a color space from the given \a stream and stores it in the given
+ \a colorSpace.
+
+ \sa QColorSpace::fromIccProfile(), {Serializing Qt Data Types}
+*/
+
+QDataStream &operator>>(QDataStream &s, QColorSpace &colorSpace)
+{
+ QByteArray iccProfile;
+ s >> iccProfile;
+ colorSpace = QColorSpace::fromIccProfile(iccProfile);
+ return s;
+}
+#endif // QT_NO_DATASTREAM
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug dbg, const QColorSpace &colorSpace)
+{
+ QDebugStateSaver saver(dbg);
+ dbg.nospace();
+ dbg << "QColorSpace(";
+ dbg << colorSpace.colorSpaceId() << ", " << colorSpace.gamut() << ", " << colorSpace.transferFunction();
+ dbg << ", gamma=" << colorSpace.gamma();
+ dbg << ')';
+ return dbg;
+}
+#endif
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qcolorspace.h b/src/gui/painting/qcolorspace.h
new file mode 100644
index 0000000000..56676826a9
--- /dev/null
+++ b/src/gui/painting/qcolorspace.h
@@ -0,0 +1,139 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCOLORSPACE_H
+#define QCOLORSPACE_H
+
+#include <QtGui/qtguiglobal.h>
+#include <QtGui/qcolortransform.h>
+#include <QtCore/qshareddata.h>
+
+QT_BEGIN_NAMESPACE
+
+class QColorSpacePrivate;
+
+class Q_GUI_EXPORT QColorSpace
+{
+ Q_GADGET
+public:
+ enum ColorSpaceId {
+ Undefined = 0,
+ Unknown = 1,
+ SRgb,
+ SRgbLinear,
+ AdobeRgb,
+ DisplayP3,
+ ProPhotoRgb,
+ Bt2020,
+ };
+ Q_ENUM(ColorSpaceId)
+ enum class Gamut {
+ Custom = 0,
+ SRgb,
+ AdobeRgb,
+ DciP3D65,
+ ProPhotoRgb,
+ Bt2020,
+ };
+ Q_ENUM(Gamut)
+ enum class TransferFunction {
+ Custom = 0,
+ Linear,
+ Gamma,
+ SRgb,
+ ProPhotoRgb,
+ Bt2020,
+ };
+ Q_ENUM(TransferFunction)
+
+ QColorSpace(ColorSpaceId colorSpaceId = Undefined);
+ QColorSpace(Gamut gamut, TransferFunction fun, float gamma = 0.0f);
+ QColorSpace(Gamut gamut, float gamma);
+ QColorSpace(const QPointF &whitePoint, const QPointF &redPoint,
+ const QPointF &greenPoint, const QPointF &bluePoint,
+ TransferFunction fun, float gamma = 0.0f);
+ ~QColorSpace();
+
+ QColorSpace(QColorSpace &&colorSpace);
+ QColorSpace(const QColorSpace &colorSpace);
+ QColorSpace &operator=(QColorSpace &&colorSpace);
+ QColorSpace &operator=(const QColorSpace &colorSpace);
+
+ ColorSpaceId colorSpaceId() const noexcept;
+ Gamut gamut() const noexcept;
+ TransferFunction transferFunction() const noexcept;
+ float gamma() const noexcept;
+
+ bool isValid() const noexcept;
+
+ friend Q_GUI_EXPORT bool operator==(const QColorSpace &colorSpace1, const QColorSpace &colorSpace2);
+ friend inline bool operator!=(const QColorSpace &colorSpace1, const QColorSpace &colorSpace2);
+
+ static QColorSpace fromIccProfile(const QByteArray &iccProfile);
+ QByteArray iccProfile() const;
+
+ QColorTransform transformationToColorSpace(const QColorSpace &colorspace) const;
+
+ QColorSpacePrivate *d_func();
+ inline const QColorSpacePrivate *d_func() const { return d_ptr.constData(); }
+
+private:
+ friend class QColorSpacePrivate;
+ QExplicitlySharedDataPointer<QColorSpacePrivate> d_ptr;
+};
+
+bool Q_GUI_EXPORT operator==(const QColorSpace &colorSpace1, const QColorSpace &colorSpace2);
+inline bool operator!=(const QColorSpace &colorSpace1, const QColorSpace &colorSpace2)
+{
+ return !(colorSpace1 == colorSpace2);
+}
+
+// QColorSpace stream functions
+#if !defined(QT_NO_DATASTREAM)
+Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QColorSpace &);
+Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QColorSpace &);
+#endif
+
+#ifndef QT_NO_DEBUG_STREAM
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QColorSpace &);
+#endif
+
+QT_END_NAMESPACE
+
+#endif // QCOLORSPACE_P_H
diff --git a/src/gui/painting/qcolorspace_p.h b/src/gui/painting/qcolorspace_p.h
new file mode 100644
index 0000000000..a49c46f195
--- /dev/null
+++ b/src/gui/painting/qcolorspace_p.h
@@ -0,0 +1,138 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCOLORSPACE_P_H
+#define QCOLORSPACE_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 "qcolorspace.h"
+#include "qcolormatrix_p.h"
+#include "qcolortrc_p.h"
+#include "qcolortrclut_p.h"
+
+#include <QtCore/qmutex.h>
+#include <QtCore/qpoint.h>
+#include <QtCore/qshareddata.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_GUI_EXPORT QColorSpacePrimaries
+{
+public:
+ QColorSpacePrimaries() = default;
+ QColorSpacePrimaries(QColorSpace::Gamut gamut);
+ QColorSpacePrimaries(QPointF whitePoint,
+ QPointF redPoint,
+ QPointF greenPoint,
+ QPointF bluePoint)
+ : whitePoint(whitePoint)
+ , redPoint(redPoint)
+ , greenPoint(greenPoint)
+ , bluePoint(bluePoint)
+ { }
+
+ QColorMatrix toXyzMatrix() const;
+ bool areValid() const;
+
+ QPointF whitePoint;
+ QPointF redPoint;
+ QPointF greenPoint;
+ QPointF bluePoint;
+};
+
+class QColorSpacePrivate : public QSharedData
+{
+public:
+ QColorSpacePrivate();
+ QColorSpacePrivate(QColorSpace::ColorSpaceId colorSpaceId);
+ QColorSpacePrivate(QColorSpace::Gamut gamut, QColorSpace::TransferFunction fun, float gamma);
+ QColorSpacePrivate(const QColorSpacePrimaries &primaries, QColorSpace::TransferFunction fun, float gamma);
+ QColorSpacePrivate(const QColorSpacePrivate &other) = default;
+
+ void initialize();
+ void setToXyzMatrix();
+ void setTransferFunction();
+ bool identifyColorSpace();
+ QColorTransform transformationToColorSpace(const QColorSpacePrivate *out) const;
+
+ QColorSpace::ColorSpaceId id;
+ QColorSpace::Gamut gamut;
+ QColorSpace::TransferFunction transferFunction;
+ float gamma;
+ QColorVector whitePoint;
+
+ QColorTrc trc[3];
+ QColorMatrix toXyz;
+
+ QString description;
+ QByteArray iccProfile;
+
+ static QBasicMutex s_lutWriteLock;
+ struct LUT {
+ LUT() = default;
+ ~LUT() = default;
+ LUT(const LUT &other)
+ {
+ if (other.generated.loadAcquire()) {
+ table[0] = other.table[0];
+ table[1] = other.table[1];
+ table[2] = other.table[2];
+ generated.store(1);
+ }
+ }
+ QSharedPointer<QColorTrcLut> &operator[](int i) { return table[i]; }
+ const QSharedPointer<QColorTrcLut> &operator[](int i) const { return table[i]; }
+ QSharedPointer<QColorTrcLut> table[3];
+ QAtomicInt generated;
+ } mutable lut;
+};
+
+QT_END_NAMESPACE
+
+#endif // QCOLORSPACE_P_H
diff --git a/src/gui/painting/qcolortransferfunction_p.h b/src/gui/painting/qcolortransferfunction_p.h
new file mode 100644
index 0000000000..fd7cfa2b2b
--- /dev/null
+++ b/src/gui/painting/qcolortransferfunction_p.h
@@ -0,0 +1,207 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCOLORTRANSFERFUNCTION_P_H
+#define QCOLORTRANSFERFUNCTION_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 <cmath>
+
+QT_BEGIN_NAMESPACE
+
+// Defines a ICC parametric curve type 4
+class Q_GUI_EXPORT 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)
+ { }
+ 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)
+ { }
+
+ bool isGamma() const
+ {
+ updateHints();
+ return m_flags & quint32(Hints::IsGamma);
+ }
+ bool isLinear() const
+ {
+ updateHints();
+ return m_flags & quint32(Hints::IsLinear);
+ }
+ bool isSRgb() const
+ {
+ updateHints();
+ return m_flags & quint32(Hints::IsSRgb);
+ }
+
+ float apply(float x) const
+ {
+ if (x < m_d)
+ return m_c * x + m_f;
+ else
+ return std::pow(m_a * x + m_b, m_g) + m_e;
+ }
+
+ QColorTransferFunction inverted() const
+ {
+ float a, b, c, d, e, f, g;
+
+ d = m_c * m_d + m_f;
+
+ if (!qFuzzyIsNull(m_c)) {
+ c = 1.0f / m_c;
+ f = -m_f / m_c;
+ } else {
+ c = 0.0f;
+ f = 0.0f;
+ }
+
+ if (!qFuzzyIsNull(m_a) && !qFuzzyIsNull(m_g)) {
+ a = std::pow(1.0f / m_a, m_g);
+ b = -a * m_e;
+ e = -m_b / m_a;
+ g = 1.0f / m_g;
+ } else {
+ a = 0.0f;
+ b = 0.0f;
+ e = 1.0f;
+ g = 1.0f;
+ }
+
+ return QColorTransferFunction(a, b, c, d, e, f, g);
+ }
+
+ // A few predefined curves:
+ static QColorTransferFunction fromGamma(float gamma)
+ {
+ return QColorTransferFunction(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, gamma);
+ }
+ 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);
+ }
+ static QColorTransferFunction fromBt2020()
+ {
+ return QColorTransferFunction(1.0f / 1.0993f, 0.0993f / 1.0993f, 1.0f / 4.5f, 0.08145f, 0.0f, 0.0f, 2.2f);
+ }
+ static QColorTransferFunction fromProPhotoRgb()
+ {
+ return QColorTransferFunction(1.0f, 0.0f, 1.0f / 16.0f, 16.0f / 512.0f, 0.0f, 0.0f, 1.8f);
+ }
+ bool matches(const QColorTransferFunction &o) const
+ {
+ return paramCompare(m_a, o.m_a) && paramCompare(m_b, o.m_b)
+ && paramCompare(m_c, o.m_c) && paramCompare(m_d, o.m_d)
+ && paramCompare(m_e, o.m_e) && paramCompare(m_f, o.m_f)
+ && paramCompare(m_g, o.m_g);
+ }
+ friend inline bool operator==(const QColorTransferFunction &f1, const QColorTransferFunction &f2);
+ friend inline bool operator!=(const QColorTransferFunction &f1, const QColorTransferFunction &f2);
+
+ float m_a;
+ float m_b;
+ float m_c;
+ float m_d;
+ float m_e;
+ float m_f;
+ float m_g;
+
+private:
+ static inline bool paramCompare(float p1, float p2)
+ {
+ // Much fuzzier than fuzzy compare.
+ // It tries match parameters that has been passed through a 8.8
+ // fixed point form.
+ return (qAbs(p1 - p2) <= (1.0f / 512.0f));
+ }
+
+ void updateHints() const
+ {
+ if (m_flags & quint32(Hints::Calculated))
+ return;
+ // We do not consider the case with m_d = 1.0f linear or simple,
+ // since it wouldn't be linear for applyExtended().
+ bool simple = paramCompare(m_a, 1.0f) && paramCompare(m_b, 0.0f)
+ && paramCompare(m_d, 0.0f)
+ && paramCompare(m_e, 0.0f);
+ if (simple) {
+ m_flags |= quint32(Hints::IsGamma);
+ if (qFuzzyCompare(m_g, 1.0f))
+ m_flags |= quint32(Hints::IsLinear);
+ } else {
+ if (*this == fromSRgb())
+ m_flags |= quint32(Hints::IsSRgb);
+ }
+ m_flags |= quint32(Hints::Calculated);
+ }
+ enum class Hints : quint32 {
+ Calculated = 1,
+ IsGamma = 2,
+ IsLinear = 4,
+ IsSRgb = 8
+ };
+ mutable quint32 m_flags;
+};
+
+inline bool operator==(const QColorTransferFunction &f1, const QColorTransferFunction &f2)
+{
+ return f1.matches(f2);
+}
+inline bool operator!=(const QColorTransferFunction &f1, const QColorTransferFunction &f2)
+{
+ return !f1.matches(f2);
+}
+
+QT_END_NAMESPACE
+
+#endif // QCOLORTRANSFERFUNCTION_P_H
diff --git a/src/gui/painting/qcolortransfertable_p.h b/src/gui/painting/qcolortransfertable_p.h
new file mode 100644
index 0000000000..c8b2f7bd92
--- /dev/null
+++ b/src/gui/painting/qcolortransfertable_p.h
@@ -0,0 +1,245 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCOLORTRANSFERTABLE_P_H
+#define QCOLORTRANSFERTABLE_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 "qcolortransferfunction_p.h"
+
+#include <QVector>
+#include <cmath>
+
+QT_BEGIN_NAMESPACE
+
+// Defines either an ICC TRC 'curve' or a lut8/lut16 A or B table
+class Q_GUI_EXPORT QColorTransferTable
+{
+public:
+ QColorTransferTable() noexcept
+ : m_tableSize(0)
+ { }
+ QColorTransferTable(uint32_t size, const QVector<uint8_t> &table) noexcept
+ : m_tableSize(size)
+ , m_table8(table)
+ { }
+ QColorTransferTable(uint32_t size, const QVector<uint16_t> &table) noexcept
+ : m_tableSize(size)
+ , m_table16(table)
+ { }
+
+ bool isValid() const
+ {
+ if (m_tableSize < 2)
+ return false;
+
+#if !defined(QT_NO_DEBUG)
+ // The table must describe an injective curve:
+ if (!m_table8.isEmpty()) {
+ uint8_t val = 0;
+ for (uint i = 0; i < m_tableSize; ++i) {
+ Q_ASSERT(m_table8[i] >= val);
+ val = m_table8[i];
+ }
+ }
+ if (!m_table16.isEmpty()) {
+ uint16_t val = 0;
+ for (uint i = 0; i < m_tableSize; ++i) {
+ Q_ASSERT(m_table16[i] >= val);
+ val = m_table16[i];
+ }
+ }
+#endif
+ return !m_table8.isEmpty() || !m_table16.isEmpty();
+ }
+
+ float apply(float x) const
+ {
+ x = std::min(std::max(x, 0.0f), 1.0f);
+ x *= m_tableSize - 1;
+ uint32_t lo = (int)std::floor(x);
+ uint32_t hi = std::min(lo + 1, m_tableSize);
+ float frac = x - lo;
+ if (!m_table16.isEmpty())
+ return (m_table16[lo] * (1.0f - frac) + m_table16[hi] * 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 x;
+ }
+
+ // Apply inverse, optimized by giving a previous result a value < x.
+ float applyInverse(float x, float resultLargerThan = 0.0f) const
+ {
+ Q_ASSERT(resultLargerThan >= 0.0f && resultLargerThan <= 1.0f);
+ if (x <= 0.0f)
+ return 0.0f;
+ if (x >= 1.0f)
+ return 1.0f;
+ if (!m_table16.isEmpty()) {
+ float v = x * 65535.0f;
+ uint i = std::floor(resultLargerThan * (m_tableSize - 1)) + 1;
+ for ( ; i < m_tableSize; ++i) {
+ if (m_table16[i] > v)
+ break;
+ }
+ if (i >= m_tableSize - 1)
+ return 1.0f;
+ float y1 = m_table16[i - 1];
+ float y2 = m_table16[i];
+ Q_ASSERT(x >= y1 && x < y2);
+ float fr = (v - y1) / (y2 - y1);
+ return (i + fr) * (1.0f / (m_tableSize - 1));
+
+ }
+ if (!m_table8.isEmpty()) {
+ float v = x * 255.0f;
+ uint i = std::floor(resultLargerThan * (m_tableSize - 1)) + 1;
+ for ( ; i < m_tableSize; ++i) {
+ if (m_table8[i] > v)
+ break;
+ }
+ if (i >= m_tableSize - 1)
+ return 1.0f;
+ float y1 = m_table8[i - 1];
+ float y2 = m_table8[i];
+ Q_ASSERT(x >= y1 && x < y2);
+ float fr = (v - y1) / (y2 - y1);
+ return (i + fr) * (1.0f / (m_tableSize - 1));
+ }
+ return x;
+ }
+
+ bool asColorTransferFunction(QColorTransferFunction *transferFn)
+ {
+ Q_ASSERT(isValid());
+ Q_ASSERT(transferFn);
+ if (!m_table8.isEmpty() && (m_table8[0] != 0 || m_table8[m_tableSize - 1] != 255))
+ return false;
+ if (!m_table16.isEmpty() && (m_table16[0] != 0 || m_table16[m_tableSize - 1] != 65535))
+ return false;
+ if (m_tableSize == 2) {
+ *transferFn = QColorTransferFunction(); // Linear
+ return true;
+ }
+ // The following heuristics are based on those from Skia:
+ if (m_tableSize == 26 && !m_table16.isEmpty()) {
+ // code.facebook.com/posts/411525055626587/under-the-hood-improving-facebook-photos
+ if (m_table16[6] != 3062)
+ return false;
+ if (m_table16[12] != 12824)
+ return false;
+ if (m_table16[18] != 31237)
+ return false;
+ *transferFn = QColorTransferFunction::fromSRgb();
+ return true;
+ }
+ if (m_tableSize == 1024 && !m_table16.isEmpty()) {
+ // HP and Canon sRGB gamma tables:
+ if (m_table16[257] != 3366)
+ return false;
+ if (m_table16[513] != 14116)
+ return false;
+ if (m_table16[768] != 34318)
+ return false;
+ *transferFn = QColorTransferFunction::fromSRgb();
+ return true;
+ }
+ if (m_tableSize == 4096 && !m_table16.isEmpty()) {
+ // Nikon, Epson, and lcms2 sRGB gamma tables:
+ if (m_table16[515] != 960)
+ return false;
+ if (m_table16[1025] != 3342)
+ return false;
+ if (m_table16[2051] != 14079)
+ return false;
+ *transferFn = QColorTransferFunction::fromSRgb();
+ return true;
+ }
+ return false;
+ }
+ friend inline bool operator!=(const QColorTransferTable &t1, const QColorTransferTable &t2);
+ friend inline bool operator==(const QColorTransferTable &t1, const QColorTransferTable &t2);
+
+ uint32_t m_tableSize;
+ QVector<uint8_t> m_table8;
+ QVector<uint16_t> m_table16;
+};
+
+inline bool operator!=(const QColorTransferTable &t1, const QColorTransferTable &t2)
+{
+ if (t1.m_tableSize != t2.m_tableSize)
+ return true;
+ if (t1.m_table8.isEmpty() != t2.m_table8.isEmpty())
+ return true;
+ if (t1.m_table16.isEmpty() != t2.m_table16.isEmpty())
+ return true;
+ if (!t1.m_table8.isEmpty()) {
+ for (quint32 i = 0; i < t1.m_tableSize; ++i) {
+ if (t1.m_table8[i] != t2.m_table8[i])
+ return true;
+ }
+ }
+ if (!t1.m_table16.isEmpty()) {
+ for (quint32 i = 0; i < t1.m_tableSize; ++i) {
+ if (t1.m_table16[i] != t2.m_table16[i])
+ return true;
+ }
+ }
+ return false;
+}
+
+inline bool operator==(const QColorTransferTable &t1, const QColorTransferTable &t2)
+{
+ return !(t1 != t2);
+}
+
+QT_END_NAMESPACE
+
+#endif // QCOLORTRANSFERTABLE_P_H
diff --git a/src/gui/painting/qcolortransform.cpp b/src/gui/painting/qcolortransform.cpp
new file mode 100644
index 0000000000..2f81449693
--- /dev/null
+++ b/src/gui/painting/qcolortransform.cpp
@@ -0,0 +1,703 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include "qcolortransform.h"
+#include "qcolortransform_p.h"
+
+#include "qcolormatrix_p.h"
+#include "qcolorspace_p.h"
+#include "qcolortrc_p.h"
+#include "qcolortrclut_p.h"
+
+#include <QtCore/qatomic.h>
+#include <QtCore/qmath.h>
+#include <QtGui/qcolor.h>
+#include <QtGui/qtransform.h>
+#include <QtCore/private/qsimd_p.h>
+
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+QColorTrcLut *lutFromTrc(const QColorTrc &trc)
+{
+ if (trc.m_type == QColorTrc::Type::Table)
+ return QColorTrcLut::fromTransferTable(trc.m_table);
+ if (trc.m_type == QColorTrc::Type::Function)
+ return QColorTrcLut::fromTransferFunction(trc.m_fun);
+ qWarning() << "TRC uninitialized";
+ return nullptr;
+}
+
+void QColorTransformPrivate::updateLutsIn() const
+{
+ if (colorSpaceIn->lut.generated.loadAcquire())
+ return;
+ QMutexLocker lock(&QColorSpacePrivate::s_lutWriteLock);
+ if (colorSpaceIn->lut.generated.load())
+ return;
+
+ for (int i = 0; i < 3; ++i) {
+ if (!colorSpaceIn->trc[i].isValid())
+ return;
+ }
+
+ if (colorSpaceIn->trc[0] == colorSpaceIn->trc[1] && colorSpaceIn->trc[0] == colorSpaceIn->trc[2]) {
+ colorSpaceIn->lut[0].reset(lutFromTrc(colorSpaceIn->trc[0]));
+ colorSpaceIn->lut[1] = colorSpaceIn->lut[0];
+ colorSpaceIn->lut[2] = colorSpaceIn->lut[0];
+ } else {
+ for (int i = 0; i < 3; ++i)
+ colorSpaceIn->lut[i].reset(lutFromTrc(colorSpaceIn->trc[i]));
+ }
+
+ colorSpaceIn->lut.generated.storeRelease(1);
+}
+
+void QColorTransformPrivate::updateLutsOut() const
+{
+ if (colorSpaceOut->lut.generated.loadAcquire())
+ return;
+ QMutexLocker lock(&QColorSpacePrivate::s_lutWriteLock);
+ if (colorSpaceOut->lut.generated.load())
+ return;
+ for (int i = 0; i < 3; ++i) {
+ if (!colorSpaceOut->trc[i].isValid())
+ return;
+ }
+
+ if (colorSpaceOut->trc[0] == colorSpaceOut->trc[1] && colorSpaceOut->trc[0] == colorSpaceOut->trc[2]) {
+ colorSpaceOut->lut[0].reset(lutFromTrc(colorSpaceOut->trc[0]));
+ colorSpaceOut->lut[1] = colorSpaceOut->lut[0];
+ colorSpaceOut->lut[2] = colorSpaceOut->lut[0];
+ } else {
+ for (int i = 0; i < 3; ++i)
+ colorSpaceOut->lut[i].reset(lutFromTrc(colorSpaceOut->trc[i]));
+ }
+
+ colorSpaceOut->lut.generated.storeRelease(1);
+}
+
+/*!
+ \class QColorTransform
+ \brief The QColorTransform class is a transformation between color spaces.
+ \since 5.14
+
+ \ingroup painting
+ \ingroup appearance
+ \inmodule QtGui
+
+ QColorTransform is an instantiation of a transformation between color spaces.
+ It can be applied on color and pixels to convert them from one color space to
+ another.
+
+ Setting up a QColorTransform takes some preprocessing, so keeping around
+ QColorTransforms that you need often is recommended, instead of generating
+ them on the fly.
+*/
+
+
+QColorTransform::~QColorTransform() noexcept
+{
+}
+
+/*!
+ Applies the color transformation on the QRgb value \a argb.
+
+ The input should be opaque or unpremultiplied.
+*/
+QRgb QColorTransform::map(const QRgb &argb) const
+{
+ if (!d_ptr)
+ return argb;
+ Q_D(const QColorTransform);
+ constexpr float f = 1.0f / 255.0f;
+ QColorVector c = { qRed(argb) * f, qGreen(argb) * f, qBlue(argb) * f };
+ 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);
+ }
+
+ return qRgba(c.x * 255 + 0.5f, c.y * 255 + 0.5f, c.z * 255 + 0.5f, qAlpha(argb));
+}
+
+/*!
+ Applies the color transformation on the QRgba64 value \a rgba64.
+
+ The input should be opaque or unpremultiplied.
+*/
+QRgba64 QColorTransform::map(const QRgba64 &rgba64) const
+{
+ if (!d_ptr)
+ return rgba64;
+ Q_D(const QColorTransform);
+ constexpr float f = 1.0f / 65535.0f;
+ QColorVector c = { rgba64.red() * f, rgba64.green() * f, rgba64.blue() * f };
+ 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);
+ }
+
+ return QRgba64::fromRgba64(c.x * 65535, c.y * 65535, c.z * 65535, rgba64.alpha());
+}
+
+/*!
+ Applies the color transformation on the QColor value \a color.
+
+*/
+QColor QColorTransform::map(const QColor &color) const
+{
+ if (!d_ptr)
+ return color;
+ Q_D(const QColorTransform);
+ 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_ptr->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);
+ }
+ QColor out;
+ out.setRgbF(c.x, c.y, c.z, color.alphaF());
+ return out;
+}
+
+// Optimized sub-routines for fast block based conversion:
+
+static void applyMatrix(QColorVector *buffer, const qsizetype len, const QColorMatrix &colorMatrix)
+{
+#if defined(__SSE2__)
+ const __m128 minV = _mm_set1_ps(0.0f);
+ const __m128 maxV = _mm_set1_ps(1.0f);
+ const __m128 xMat = _mm_loadu_ps(&colorMatrix.r.x);
+ const __m128 yMat = _mm_loadu_ps(&colorMatrix.g.x);
+ const __m128 zMat = _mm_loadu_ps(&colorMatrix.b.x);
+ for (qsizetype j = 0; j < len; ++j) {
+ __m128 c = _mm_loadu_ps(&buffer[j].x);
+ __m128 cx = _mm_shuffle_ps(c, c, _MM_SHUFFLE(0, 0, 0, 0));
+ __m128 cy = _mm_shuffle_ps(c, c, _MM_SHUFFLE(1, 1, 1, 1));
+ __m128 cz = _mm_shuffle_ps(c, c, _MM_SHUFFLE(2, 2, 2, 2));
+ cx = _mm_mul_ps(cx, xMat);
+ cy = _mm_mul_ps(cy, yMat);
+ cz = _mm_mul_ps(cz, zMat);
+ cx = _mm_add_ps(cx, cy);
+ cx = _mm_add_ps(cx, cz);
+ // Clamp:
+ cx = _mm_min_ps(cx, maxV);
+ cx = _mm_max_ps(cx, minV);
+ _mm_storeu_ps(&buffer[j].x, cx);
+ }
+#else
+ for (int j = 0; j < len; ++j) {
+ const QColorVector cv = colorMatrix.map(buffer[j]);
+ 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));
+ }
+#endif
+}
+
+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);
+
+#if defined(__SSE2__)
+// Load to [0-alpha] in 4x32 SIMD
+template<typename T>
+static inline void loadP(const T &p, __m128i &v);
+
+template<>
+inline void loadP<QRgb>(const QRgb &p, __m128i &v)
+{
+ v = _mm_cvtsi32_si128(p);
+#if defined(__SSE4_1__)
+ v = _mm_cvtepu8_epi32(v);
+#else
+ v = _mm_unpacklo_epi8(v, _mm_setzero_si128());
+ v = _mm_unpacklo_epi16(v, _mm_setzero_si128());
+#endif
+}
+
+template<>
+inline void loadP<QRgba64>(const QRgba64 &p, __m128i &v)
+{
+ v = _mm_loadl_epi64((const __m128i *)&p);
+#if defined(__SSE4_1__)
+ v = _mm_cvtepu16_epi32(v);
+#else
+ v = _mm_unpacklo_epi16(v, _mm_setzero_si128());
+#endif
+ // Shuffle to ARGB as the template below expects it
+ v = _mm_shuffle_epi32(v, _MM_SHUFFLE(3, 0, 1, 2));
+}
+
+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 iFF00 = _mm_set1_ps(1.0f / (255 * 256));
+ for (qsizetype i = 0; i < len; ++i) {
+ __m128i v;
+ loadP<T>(src[i], v);
+ __m128 vf = _mm_cvtepi32_ps(v);
+ // Approximate 1/a:
+ __m128 va = _mm_shuffle_ps(vf, vf, _MM_SHUFFLE(3, 3, 3, 3));
+ __m128 via = _mm_rcp_ps(va);
+ via = _mm_sub_ps(_mm_add_ps(via, via), _mm_mul_ps(via, _mm_mul_ps(via, va)));
+ // v * (1/a)
+ vf = _mm_mul_ps(vf, via);
+
+ // Handle zero alpha
+ __m128 vAlphaMask = _mm_cmpeq_ps(va, _mm_set1_ps(0.0f));
+ vf = _mm_andnot_ps(vAlphaMask, vf);
+
+ // LUT
+ v = _mm_cvtps_epi32(_mm_mul_ps(vf, v4080));
+ const int ridx = _mm_extract_epi16(v, 4);
+ const int gidx = _mm_extract_epi16(v, 2);
+ const int bidx = _mm_extract_epi16(v, 0);
+ v = _mm_insert_epi16(v, d_ptr->colorSpaceIn->lut[0]->m_toLinear[ridx], 0);
+ v = _mm_insert_epi16(v, d_ptr->colorSpaceIn->lut[1]->m_toLinear[gidx], 2);
+ v = _mm_insert_epi16(v, d_ptr->colorSpaceIn->lut[2]->m_toLinear[bidx], 4);
+ vf = _mm_mul_ps(_mm_cvtepi32_ps(v), iFF00);
+
+ _mm_storeu_ps(&buffer[i].x, vf);
+ }
+}
+
+// Load to [0-4080] in 4x32 SIMD
+template<typename T>
+static inline void loadPU(const T &p, __m128i &v);
+
+template<>
+inline void loadPU<QRgb>(const QRgb &p, __m128i &v)
+{
+ v = _mm_cvtsi32_si128(p);
+#if defined(__SSE4_1__)
+ v = _mm_cvtepu8_epi32(v);
+#else
+ v = _mm_unpacklo_epi8(v, _mm_setzero_si128());
+ v = _mm_unpacklo_epi16(v, _mm_setzero_si128());
+#endif
+ v = _mm_slli_epi32(v, 4);
+}
+
+template<>
+inline void loadPU<QRgba64>(const QRgba64 &p, __m128i &v)
+{
+ v = _mm_loadl_epi64((const __m128i *)&p);
+ v = _mm_sub_epi16(v, _mm_srli_epi16(v, 8));
+#if defined(__SSE4_1__)
+ v = _mm_cvtepu16_epi32(v);
+#else
+ v = _mm_unpacklo_epi16(v, _mm_setzero_si128());
+#endif
+ v = _mm_srli_epi32(v, 4);
+ // Shuffle to ARGB as the template below expects it
+ v = _mm_shuffle_epi32(v, _MM_SHUFFLE(3, 0, 1, 2));
+}
+
+template<typename T>
+void loadUnpremultiplied(QColorVector *buffer, const T *src, const qsizetype len, const QColorTransformPrivate *d_ptr)
+{
+ const __m128 iFF00 = _mm_set1_ps(1.0f / (255 * 256));
+ for (qsizetype i = 0; i < len; ++i) {
+ __m128i v;
+ loadPU<T>(src[i], v);
+ const int ridx = _mm_extract_epi16(v, 4);
+ const int gidx = _mm_extract_epi16(v, 2);
+ const int bidx = _mm_extract_epi16(v, 0);
+ v = _mm_insert_epi16(v, d_ptr->colorSpaceIn->lut[0]->m_toLinear[ridx], 0);
+ v = _mm_insert_epi16(v, d_ptr->colorSpaceIn->lut[1]->m_toLinear[gidx], 2);
+ v = _mm_insert_epi16(v, d_ptr->colorSpaceIn->lut[2]->m_toLinear[bidx], 4);
+ __m128 vf = _mm_mul_ps(_mm_cvtepi32_ps(v), iFF00);
+ _mm_storeu_ps(&buffer[i].x, vf);
+ }
+}
+
+#else
+template<>
+void loadPremultiplied<QRgb>(QColorVector *buffer, const QRgb *src, const qsizetype len, const QColorTransformPrivate *d_ptr)
+{
+ for (qsizetype i = 0; i < len; ++i) {
+ const uint p = src[i];
+ const int a = qAlpha(p);
+ if (a) {
+ const float ia = 4080.0f / 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);
+ buffer[i].x = d_ptr->colorSpaceIn->lut[0]->m_toLinear[ridx] * (1.0f / (255 * 256));
+ buffer[i].y = d_ptr->colorSpaceIn->lut[1]->m_toLinear[gidx] * (1.0f / (255 * 256));
+ buffer[i].z = d_ptr->colorSpaceIn->lut[2]->m_toLinear[bidx] * (1.0f / (255 * 256));
+ } else {
+ buffer[i].x = buffer[i].y = buffer[i].z = 0.0f;
+ }
+ }
+}
+
+template<>
+void loadPremultiplied<QRgba64>(QColorVector *buffer, const QRgba64 *src, const qsizetype len, const QColorTransformPrivate *d_ptr)
+{
+ for (qsizetype i = 0; i < len; ++i) {
+ const QRgba64 &p = src[i];
+ const int a = p.alpha();
+ if (a) {
+ const float ia = 4080.0f / 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);
+ buffer[i].x = d_ptr->colorSpaceIn->lut[0]->m_toLinear[ridx] * (1.0f / (255 * 256));
+ buffer[i].y = d_ptr->colorSpaceIn->lut[1]->m_toLinear[gidx] * (1.0f / (255 * 256));
+ buffer[i].z = d_ptr->colorSpaceIn->lut[2]->m_toLinear[bidx] * (1.0f / (255 * 256));
+ } else {
+ buffer[i].x = buffer[i].y = buffer[i].z = 0.0f;
+ }
+ }
+}
+
+template<>
+void loadUnpremultiplied<QRgb>(QColorVector *buffer, const QRgb *src, const qsizetype len, const QColorTransformPrivate *d_ptr)
+{
+ for (qsizetype i = 0; i < len; ++i) {
+ const uint p = src[i];
+ buffer[i].x = d_ptr->colorSpaceIn->lut[0]->u8ToLinearF32(qRed(p));
+ buffer[i].y = d_ptr->colorSpaceIn->lut[1]->u8ToLinearF32(qGreen(p));
+ buffer[i].z = d_ptr->colorSpaceIn->lut[2]->u8ToLinearF32(qBlue(p));
+ }
+}
+
+template<>
+void loadUnpremultiplied<QRgba64>(QColorVector *buffer, const QRgba64 *src, const qsizetype len, const QColorTransformPrivate *d_ptr)
+{
+ for (qsizetype i = 0; i < len; ++i) {
+ const QRgba64 &p = src[i];
+ buffer[i].x = d_ptr->colorSpaceIn->lut[0]->u16ToLinearF32(p.red());
+ buffer[i].y = d_ptr->colorSpaceIn->lut[1]->u16ToLinearF32(p.green());
+ buffer[i].z = d_ptr->colorSpaceIn->lut[2]->u16ToLinearF32(p.blue());
+ }
+}
+#endif
+
+static void storePremultiplied(QRgb *dst, const QRgb *src, const QColorVector *buffer, const qsizetype len,
+ const QColorTransformPrivate *d_ptr)
+{
+#if defined(__SSE2__)
+ const __m128 v4080 = _mm_set1_ps(4080.f);
+ const __m128 iFF00 = _mm_set1_ps(1.0f / (255 * 256));
+ for (qsizetype i = 0; i < len; ++i) {
+ const int a = qAlpha(src[i]);
+ __m128 vf = _mm_loadu_ps(&buffer[i].x);
+ __m128i v = _mm_cvtps_epi32(_mm_mul_ps(vf, v4080));
+ __m128 va = _mm_set1_ps(a);
+ va = _mm_mul_ps(va, iFF00);
+ const int ridx = _mm_extract_epi16(v, 0);
+ const int gidx = _mm_extract_epi16(v, 2);
+ const int bidx = _mm_extract_epi16(v, 4);
+ v = _mm_insert_epi16(v, d_ptr->colorSpaceOut->lut[0]->m_fromLinear[ridx], 4);
+ v = _mm_insert_epi16(v, d_ptr->colorSpaceOut->lut[1]->m_fromLinear[gidx], 2);
+ v = _mm_insert_epi16(v, d_ptr->colorSpaceOut->lut[2]->m_fromLinear[bidx], 0);
+ vf = _mm_cvtepi32_ps(v);
+ vf = _mm_mul_ps(vf, va);
+ v = _mm_cvtps_epi32(vf);
+ v = _mm_packs_epi32(v, v);
+ v = _mm_insert_epi16(v, a, 3);
+ v = _mm_packus_epi16(v, v);
+ dst[i] = _mm_cvtsi128_si32(v);
+ }
+#else
+ 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)];
+ dst[i] = qRgba(r * fa + 0.5f, g * fa + 0.5f, b * fa + 0.5f, a);
+ }
+#endif
+}
+
+static void storeUnpremultiplied(QRgb *dst, const QRgb *src, const QColorVector *buffer, const qsizetype len,
+ const QColorTransformPrivate *d_ptr)
+{
+#if defined(__SSE2__)
+ const __m128 v4080 = _mm_set1_ps(4080.f);
+ for (qsizetype i = 0; i < len; ++i) {
+ const int a = qAlpha(src[i]);
+ __m128 vf = _mm_loadu_ps(&buffer[i].x);
+ __m128i v = _mm_cvtps_epi32(_mm_mul_ps(vf, v4080));
+ const int ridx = _mm_extract_epi16(v, 0);
+ const int gidx = _mm_extract_epi16(v, 2);
+ const int bidx = _mm_extract_epi16(v, 4);
+ v = _mm_setzero_si128();
+ v = _mm_insert_epi16(v, d_ptr->colorSpaceOut->lut[0]->m_fromLinear[ridx], 2);
+ 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], 0);
+ v = _mm_add_epi16(v, _mm_set1_epi16(0x80));
+ v = _mm_srli_epi16(v, 8);
+ v = _mm_insert_epi16(v, a, 3);
+ v = _mm_packus_epi16(v, v);
+ dst[i] = _mm_cvtsi128_si32(v);
+ }
+#else
+ 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);
+ const int b = d_ptr->colorSpaceOut->lut[2]->u8FromLinearF32(buffer[i].z);
+ dst[i] = (src[i] & 0xff000000) | (r << 16) | (g << 8) | (b << 0);
+ }
+#endif
+}
+
+static void storeOpaque(QRgb *dst, const QRgb *src, const QColorVector *buffer, const qsizetype len,
+ const QColorTransformPrivate *d_ptr)
+{
+ Q_UNUSED(src);
+#if defined(__SSE2__)
+ const __m128 v4080 = _mm_set1_ps(4080.f);
+ 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));
+ const int ridx = _mm_extract_epi16(v, 0);
+ const int gidx = _mm_extract_epi16(v, 2);
+ const int bidx = _mm_extract_epi16(v, 4);
+ v = _mm_setzero_si128();
+ v = _mm_insert_epi16(v, d_ptr->colorSpaceOut->lut[0]->m_fromLinear[ridx], 2);
+ 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], 0);
+ v = _mm_add_epi16(v, _mm_set1_epi16(0x80));
+ v = _mm_srli_epi16(v, 8);
+ v = _mm_insert_epi16(v, 255, 3);
+ v = _mm_packus_epi16(v, v);
+ dst[i] = _mm_cvtsi128_si32(v);
+ }
+#else
+ 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);
+ const int b = d_ptr->colorSpaceOut->lut[2]->u8FromLinearF32(buffer[i].z);
+ dst[i] = 0xff000000 | (r << 16) | (g << 8) | (b << 0);
+ }
+#endif
+}
+
+static void storePremultiplied(QRgba64 *dst, const QRgba64 *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 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)];
+ 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,
+ const QColorTransformPrivate *d_ptr)
+{
+ 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);
+ const int b = d_ptr->colorSpaceOut->lut[2]->u16FromLinearF32(buffer[i].z);
+ dst[i] = qRgba64(r, g, b, src[i].alpha());
+ }
+}
+
+static void storeOpaque(QRgba64 *dst, const QRgba64 *src, 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);
+ const int b = d_ptr->colorSpaceOut->lut[2]->u16FromLinearF32(buffer[i].z);
+ dst[i] = qRgba64(r, g, b, 0xFFFF);
+ }
+}
+
+static constexpr qsizetype WorkBlockSize = 256;
+
+template<typename T>
+void QColorTransformPrivate::apply(T *dst, const T *src, qsizetype count, TransformFlags flags) const
+{
+ if (!colorMatrix.isValid())
+ return;
+
+ updateLutsIn();
+ updateLutsOut();
+
+ bool doApplyMatrix = (colorMatrix != QColorMatrix::identity());
+
+ QColorVector buffer[WorkBlockSize];
+ 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(buffer, len, colorMatrix);
+
+ 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);
+
+ i += len;
+ }
+}
+
+/*!
+ \internal
+ \enum QColorTransformPrivate::TransformFlag
+
+ Defines how the transform is to be applied.
+
+ \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.
+*/
+
+/*!
+ \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.
+
+ \sa QColorTransform::map(), apply()
+*/
+void QColorTransformPrivate::prepare()
+{
+ updateLutsIn();
+ updateLutsOut();
+}
+
+/*!
+ \internal
+ Applies the color transformation on \a count QRgb pixels starting from
+ \a src and stores the result in \a dst.
+
+ Thread-safe if prepare() has been called first.
+
+ Assumes unpremultiplied data by default. Set \a flags to change defaults.
+
+ \sa prepare()
+*/
+void QColorTransformPrivate::apply(QRgb *dst, const QRgb *src, qsizetype count, TransformFlags flags) const
+{
+ apply<QRgb>(dst, src, count, flags);
+}
+
+/*!
+ \internal
+ Applies the color transformation on \a count QRgba64 pixels starting from
+ \a src and stores the result in \a dst.
+
+ Thread-safe if prepare() has been called first.
+
+ Assumes unpremultiplied data by default. Set \a flags to change defaults.
+
+ \sa prepare()
+*/
+void QColorTransformPrivate::apply(QRgba64 *dst, const QRgba64 *src, qsizetype count, TransformFlags flags) const
+{
+ apply<QRgba64>(dst, src, count, flags);
+}
+
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qcolortransform.h b/src/gui/painting/qcolortransform.h
new file mode 100644
index 0000000000..9274387b97
--- /dev/null
+++ b/src/gui/painting/qcolortransform.h
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCOLORTRANSFORM_H
+#define QCOLORTRANSFORM_H
+
+#include <QtGui/qtguiglobal.h>
+#include <QtCore/qsharedpointer.h>
+#include <QtGui/qrgb.h>
+
+QT_BEGIN_NAMESPACE
+
+class QColor;
+class QRgba64;
+class QColorSpacePrivate;
+class QColorTransformPrivate;
+
+class Q_GUI_EXPORT QColorTransform
+{
+public:
+ QColorTransform() noexcept : d_ptr(nullptr) { }
+ ~QColorTransform() noexcept;
+ QColorTransform(const QColorTransform &colorTransform) noexcept
+ : d_ptr(colorTransform.d_ptr)
+ { }
+ QColorTransform(QColorTransform &&colorTransform) noexcept
+ : d_ptr(std::move(colorTransform.d_ptr))
+ { }
+ QColorTransform &operator=(const QColorTransform &other) noexcept
+ {
+ d_ptr = other.d_ptr;
+ return *this;
+ }
+ QColorTransform &operator=(QColorTransform &&other) noexcept
+ {
+ d_ptr = std::move(other.d_ptr);
+ return *this;
+ }
+
+ bool isNull() const { return d_ptr.isNull(); }
+
+ QRgb map(const QRgb &argb) const;
+ QRgba64 map(const QRgba64 &rgba64) const;
+ QColor map(const QColor &color) const;
+
+private:
+ friend class QColorSpace;
+ friend class QColorSpacePrivate;
+ friend class QImage;
+
+ Q_DECLARE_PRIVATE(QColorTransform)
+ QSharedPointer<QColorTransformPrivate> d_ptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QCOLORTRANSFORM_H
diff --git a/src/gui/painting/qcolortransform_p.h b/src/gui/painting/qcolortransform_p.h
new file mode 100644
index 0000000000..74a1e7fe0a
--- /dev/null
+++ b/src/gui/painting/qcolortransform_p.h
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCOLORTRANSFORM_P_H
+#define QCOLORTRANSFORM_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 "qcolormatrix_p.h"
+#include "qcolorspace_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QColorTransformPrivate
+{
+public:
+ QColorMatrix colorMatrix;
+ QExplicitlySharedDataPointer<const QColorSpacePrivate> colorSpaceIn;
+ QExplicitlySharedDataPointer<const QColorSpacePrivate> colorSpaceOut;
+
+ void updateLutsIn() const;
+ void updateLutsOut() const;
+ bool simpleGammaCorrection() const;
+
+ void prepare();
+ enum TransformFlag {
+ Unpremultiplied = 0,
+ InputOpaque = 1,
+ InputPremultiplied = 2,
+ OutputPremultiplied = 4,
+ Premultiplied = (InputPremultiplied | OutputPremultiplied)
+ };
+ 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;
+
+ template<typename T>
+ void apply(T *dst, const T *src, qsizetype count, TransformFlags flags) const;
+};
+
+QT_END_NAMESPACE
+
+#endif // QCOLORTRANSFORM_P_H
diff --git a/src/gui/painting/qcolortrc_p.h b/src/gui/painting/qcolortrc_p.h
new file mode 100644
index 0000000000..3ef9d442fc
--- /dev/null
+++ b/src/gui/painting/qcolortrc_p.h
@@ -0,0 +1,148 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCOLORTRC_P_H
+#define QCOLORTRC_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 "qcolortransferfunction_p.h"
+#include "qcolortransfertable_p.h"
+
+QT_BEGIN_NAMESPACE
+
+
+// Defines an ICC 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)
+ { }
+
+ enum class Type {
+ Uninitialized,
+ Function,
+ Table
+ };
+
+ bool isLinear() const
+ {
+ return m_type == Type::Uninitialized || (m_type == Type::Function && m_fun.isLinear());
+ }
+ bool isValid() const
+ {
+ return m_type != Type::Uninitialized;
+ }
+ float apply(float x) const
+ {
+ if (m_type == Type::Table)
+ return m_table.apply(x);
+ if (m_type == Type::Function)
+ return m_fun.apply(x);
+ return x;
+ }
+ float applyExtended(float x) const
+ {
+ if (x >= 0.0f && x <= 1.0f)
+ return apply(x);
+ if (m_type == Type::Function)
+ return std::copysign(m_fun.apply(std::abs(x)), x);
+ if (m_type == Type::Table)
+ return x < 0.0f ? 0.0f : 1.0f;
+ return x;
+ }
+ float applyInverse(float x) const
+ {
+ if (m_type == Type::Table)
+ return m_table.applyInverse(x);
+ if (m_type == Type::Function)
+ return m_fun.inverted().apply(x);
+ return x;
+ }
+ float applyInverseExtended(float x) const
+ {
+ if (x >= 0.0f && x <= 1.0f)
+ return applyInverse(x);
+ if (m_type == Type::Function)
+ return std::copysign(applyInverse(x), x);
+ if (m_type == Type::Table)
+ return x < 0.0f ? 0.0f : 1.0f;
+ return x;
+ }
+
+ friend inline bool operator!=(const QColorTrc &o1, const QColorTrc &o2);
+ friend inline bool operator==(const QColorTrc &o1, const QColorTrc &o2);
+
+ Type m_type;
+ QColorTransferFunction m_fun;
+ QColorTransferTable m_table;
+};
+
+inline bool operator!=(const QColorTrc &o1, const QColorTrc &o2)
+{
+ if (o1.m_type != o2.m_type)
+ return true;
+ if (o1.m_type == QColorTrc::Type::Function)
+ return o1.m_fun != o2.m_fun;
+ if (o1.m_type == QColorTrc::Type::Table)
+ return o1.m_table != o2.m_table;
+ return false;
+}
+inline bool operator==(const QColorTrc &o1, const QColorTrc &o2)
+{
+ return !(o1 != o2);
+}
+
+QT_END_NAMESPACE
+
+#endif // QCOLORTRC
diff --git a/src/gui/painting/qcolorprofile.cpp b/src/gui/painting/qcolortrclut.cpp
index 3b7b0a248b..268d7252b4 100644
--- a/src/gui/painting/qcolorprofile.cpp
+++ b/src/gui/painting/qcolortrclut.cpp
@@ -37,14 +37,16 @@
**
****************************************************************************/
-#include "qcolorprofile_p.h"
+#include "qcolortrclut_p.h"
+#include "qcolortransferfunction_p.h"
+#include "qcolortransfertable_p.h"
#include <qmath.h>
QT_BEGIN_NAMESPACE
-QColorProfile *QColorProfile::fromGamma(qreal gamma)
+QColorTrcLut *QColorTrcLut::fromGamma(qreal gamma)
{
- QColorProfile *cp = new QColorProfile;
+ QColorTrcLut *cp = new QColorTrcLut;
for (int i = 0; i <= (255 * 16); ++i) {
cp->m_toLinear[i] = ushort(qRound(qPow(i / qreal(255 * 16), gamma) * (255 * 256)));
@@ -54,31 +56,28 @@ QColorProfile *QColorProfile::fromGamma(qreal gamma)
return cp;
}
-static qreal srgbToLinear(qreal v)
+QColorTrcLut *QColorTrcLut::fromTransferFunction(const QColorTransferFunction &fun)
{
- const qreal a = 0.055;
- if (v <= qreal(0.04045))
- return v / qreal(12.92);
- else
- return qPow((v + a) / (qreal(1) + a), qreal(2.4));
-}
+ QColorTrcLut *cp = new QColorTrcLut;
+ QColorTransferFunction inv = fun.inverted();
-static qreal linearToSrgb(qreal v)
-{
- const qreal a = 0.055;
- if (v <= qreal(0.0031308))
- return v * qreal(12.92);
- else
- return (qreal(1) + a) * qPow(v, qreal(1.0 / 2.4)) - a;
+ 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)));
+ }
+
+ return cp;
}
-QColorProfile *QColorProfile::fromSRgb()
+QColorTrcLut *QColorTrcLut::fromTransferTable(const QColorTransferTable &table)
{
- QColorProfile *cp = new QColorProfile;
+ QColorTrcLut *cp = new QColorTrcLut;
+ float minInverse = 0.0f;
for (int i = 0; i <= (255 * 16); ++i) {
- cp->m_toLinear[i] = ushort(qRound(srgbToLinear(i / qreal(255 * 16)) * (255 * 256)));
- cp->m_fromLinear[i] = ushort(qRound(linearToSrgb(i / qreal(255 * 16)) * (255 * 256)));
+ 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));
}
return cp;
diff --git a/src/gui/painting/qcolorprofile_p.h b/src/gui/painting/qcolortrclut_p.h
index 425e9abace..76a6a60803 100644
--- a/src/gui/painting/qcolorprofile_p.h
+++ b/src/gui/painting/qcolortrclut_p.h
@@ -37,8 +37,8 @@
**
****************************************************************************/
-#ifndef QCOLORPROFILE_P_H
-#define QCOLORPROFILE_P_H
+#ifndef QCOLORTRCLUT_P_H
+#define QCOLORTRCLUT_P_H
//
// W A R N I N G
@@ -52,21 +52,29 @@
//
#include <QtGui/private/qtguiglobal_p.h>
+#include <QtCore/qsharedpointer.h>
#include <QtGui/qrgb.h>
#include <QtGui/qrgba64.h>
+#include <cmath>
+
#if defined(__SSE2__)
#include <emmintrin.h>
#elif defined(__ARM_NEON__) || defined(__ARM_NEON)
#include <arm_neon.h>
#endif
+
QT_BEGIN_NAMESPACE
-class Q_GUI_EXPORT QColorProfile
+class QColorTransferFunction;
+class QColorTransferTable;
+
+class Q_GUI_EXPORT QColorTrcLut : public QEnableSharedFromThis<QColorTrcLut>
{
public:
- static QColorProfile *fromGamma(qreal gamma);
- static QColorProfile *fromSRgb();
+ static QColorTrcLut *fromGamma(qreal gamma);
+ static QColorTrcLut *fromTransferFunction(const QColorTransferFunction &transfn);
+ static QColorTrcLut *fromTransferTable(const QColorTransferTable &transTable);
// The following methods all convert opaque or unpremultiplied colors:
@@ -121,6 +129,25 @@ public:
return convertWithTable(rgb64, m_toLinear);
}
+ float u8ToLinearF32(int c) const
+ {
+ ushort v = m_toLinear[c << 4];
+ return v * (1.0f / (255*256));
+ }
+
+ float u16ToLinearF32(int c) const
+ {
+ c -= (c >> 8);
+ ushort v = m_toLinear[c >> 4];
+ return v * (1.0f / (255*256));
+ }
+
+ float toLinear(float f) const
+ {
+ ushort v = m_toLinear[(int)(f * (255 * 16) + 0.5f)];
+ return v * (1.0f / (255*256));
+ }
+
QRgb fromLinear64(QRgba64 rgb64) const
{
#if defined(__SSE2__)
@@ -176,8 +203,31 @@ public:
return convertWithTable(rgb64, m_fromLinear);
}
+ int u8FromLinearF32(float f) const
+ {
+ ushort v = m_fromLinear[(int)(f * (255 * 16) + 0.5f)];
+ return (v + 0x80) >> 8;
+ }
+ int u16FromLinearF32(float f) const
+ {
+ ushort v = m_fromLinear[(int)(f * (255 * 16) + 0.5f)];
+ return v + (v >> 8);
+ }
+ float fromLinear(float f) const
+ {
+ ushort v = m_fromLinear[(int)(f * (255 * 16) + 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]
+
private:
- QColorProfile() { }
+ QColorTrcLut() { }
Q_ALWAYS_INLINE static QRgb convertWithTable(QRgb rgb32, const ushort *table)
{
@@ -230,16 +280,8 @@ private:
return QRgba64::fromRgba64(r, g, b, rgb64.alpha());
#endif
}
-
- // 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]
-
};
QT_END_NAMESPACE
-#endif // QCOLORPROFILE_P_H
+#endif // QCOLORTRCLUT_P_H
diff --git a/src/gui/painting/qcompositionfunctions.cpp b/src/gui/painting/qcompositionfunctions.cpp
index 5c1afe8425..06a849e790 100644
--- a/src/gui/painting/qcompositionfunctions.cpp
+++ b/src/gui/painting/qcompositionfunctions.cpp
@@ -137,6 +137,7 @@ struct Rgba64OperationsBase
{ ::memcpy(dest, src, len * sizeof(Type)); }
};
+#if QT_CONFIG(raster_64bit)
const Rgba64OperationsBase::Type Rgba64OperationsBase::clear = QRgba64::fromRgba64(0);
struct Rgba64OperationsC : public Rgba64OperationsBase
@@ -309,10 +310,8 @@ struct Rgba64OperationsNEON : public Rgba64OperationsBase
return interpolate65535(x, a1, y, a2);
}
};
-
#endif
-typedef Argb32OperationsC Argb32Operations;
#if defined(__SSE2__)
typedef Rgba64OperationsSSE2 Rgba64Operations;
#elif defined(__ARM_NEON__)
@@ -320,6 +319,9 @@ typedef Rgba64OperationsNEON Rgba64Operations;
#else
typedef Rgba64OperationsC Rgba64Operations;
#endif
+#endif // QT_CONFIG(raster_64bit)
+
+typedef Argb32OperationsC Argb32Operations;
/*
result = 0
@@ -343,20 +345,23 @@ void QT_FASTCALL comp_func_solid_Clear(uint *dest, int length, uint, uint const_
comp_func_Clear_template<Argb32Operations>(dest, length, const_alpha);
}
-void QT_FASTCALL comp_func_solid_Clear_rgb64(QRgba64 *dest, int length, QRgba64, uint const_alpha)
+void QT_FASTCALL comp_func_Clear(uint *dest, const uint *, int length, uint const_alpha)
{
- comp_func_Clear_template<Rgba64Operations>(dest, length, const_alpha);
+ comp_func_Clear_template<Argb32Operations>(dest, length, const_alpha);
}
-void QT_FASTCALL comp_func_Clear(uint *dest, const uint *, int length, uint const_alpha)
+#if QT_CONFIG(raster_64bit)
+void QT_FASTCALL comp_func_solid_Clear_rgb64(QRgba64 *dest, int length, QRgba64, uint const_alpha)
{
- comp_func_Clear_template<Argb32Operations>(dest, length, const_alpha);
+ comp_func_Clear_template<Rgba64Operations>(dest, length, const_alpha);
}
void QT_FASTCALL comp_func_Clear_rgb64(QRgba64 *dest, const QRgba64 *, int length, uint const_alpha)
{
comp_func_Clear_template<Rgba64Operations>(dest, length, const_alpha);
}
+#endif
+
/*
result = s
@@ -399,36 +404,40 @@ void QT_FASTCALL comp_func_solid_Source(uint *dest, int length, uint color, uint
comp_func_solid_Source_template<Argb32Operations>(dest, length, color, const_alpha);
}
-void QT_FASTCALL comp_func_solid_Source_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
+void QT_FASTCALL comp_func_Source(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
{
- comp_func_solid_Source_template<Rgba64Operations>(dest, length, color, const_alpha);
+ comp_func_Source_template<Argb32Operations>(dest, src, length, const_alpha);
}
-void QT_FASTCALL comp_func_Source(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
+#if QT_CONFIG(raster_64bit)
+void QT_FASTCALL comp_func_solid_Source_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
{
- comp_func_Source_template<Argb32Operations>(dest, src, length, const_alpha);
+ comp_func_solid_Source_template<Rgba64Operations>(dest, length, color, const_alpha);
}
void QT_FASTCALL comp_func_Source_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha)
{
comp_func_Source_template<Rgba64Operations>(dest, src, length, const_alpha);
}
+#endif
void QT_FASTCALL comp_func_solid_Destination(uint *, int, uint, uint)
{
}
-void QT_FASTCALL comp_func_solid_Destination_rgb64(QRgba64 *, int, QRgba64, uint)
+void QT_FASTCALL comp_func_Destination(uint *, const uint *, int, uint)
{
}
-void QT_FASTCALL comp_func_Destination(uint *, const uint *, int, uint)
+#if QT_CONFIG(raster_64bit)
+void QT_FASTCALL comp_func_solid_Destination_rgb64(QRgba64 *, int, QRgba64, uint)
{
}
void QT_FASTCALL comp_func_Destination_rgb64(QRgba64 *, const QRgba64 *, int, uint)
{
}
+#endif
/*
result = s + d * sia
@@ -483,20 +492,22 @@ void QT_FASTCALL comp_func_solid_SourceOver(uint *dest, int length, uint color,
comp_func_solid_SourceOver_template<Argb32Operations>(dest, length, color, const_alpha);
}
-void QT_FASTCALL comp_func_solid_SourceOver_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
+void QT_FASTCALL comp_func_SourceOver(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
{
- comp_func_solid_SourceOver_template<Rgba64Operations>(dest, length, color, const_alpha);
+ comp_func_SourceOver_template<Argb32Operations>(dest, src, length, const_alpha);
}
-void QT_FASTCALL comp_func_SourceOver(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
+#if QT_CONFIG(raster_64bit)
+void QT_FASTCALL comp_func_solid_SourceOver_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
{
- comp_func_SourceOver_template<Argb32Operations>(dest, src, length, const_alpha);
+ comp_func_solid_SourceOver_template<Rgba64Operations>(dest, length, color, const_alpha);
}
void QT_FASTCALL comp_func_SourceOver_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha)
{
comp_func_SourceOver_template<Rgba64Operations>(dest, src, length, const_alpha);
}
+#endif
/*
result = d + s * dia
@@ -542,20 +553,22 @@ void QT_FASTCALL comp_func_solid_DestinationOver(uint *dest, int length, uint co
comp_func_solid_DestinationOver_template<Argb32Operations>(dest, length, color, const_alpha);
}
-void QT_FASTCALL comp_func_solid_DestinationOver_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
+void QT_FASTCALL comp_func_DestinationOver(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
{
- comp_func_solid_DestinationOver_template<Rgba64Operations>(dest, length, color, const_alpha);
+ comp_func_DestinationOver_template<Argb32Operations>(dest, src, length, const_alpha);
}
-void QT_FASTCALL comp_func_DestinationOver(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
+#if QT_CONFIG(raster_64bit)
+void QT_FASTCALL comp_func_solid_DestinationOver_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
{
- comp_func_DestinationOver_template<Argb32Operations>(dest, src, length, const_alpha);
+ comp_func_solid_DestinationOver_template<Rgba64Operations>(dest, length, color, const_alpha);
}
void QT_FASTCALL comp_func_DestinationOver_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha)
{
comp_func_DestinationOver_template<Rgba64Operations>(dest, src, length, const_alpha);
}
+#endif
/*
result = s * da
@@ -606,20 +619,22 @@ void QT_FASTCALL comp_func_solid_SourceIn(uint *dest, int length, uint color, ui
comp_func_solid_SourceIn_template<Argb32Operations>(dest, length, color, const_alpha);
}
-void QT_FASTCALL comp_func_solid_SourceIn_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
+void QT_FASTCALL comp_func_SourceIn(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
{
- comp_func_solid_SourceIn_template<Rgba64Operations>(dest, length, color, const_alpha);
+ comp_func_SourceIn_template<Argb32Operations>(dest, src, length, const_alpha);
}
-void QT_FASTCALL comp_func_SourceIn(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
+#if QT_CONFIG(raster_64bit)
+void QT_FASTCALL comp_func_solid_SourceIn_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
{
- comp_func_SourceIn_template<Argb32Operations>(dest, src, length, const_alpha);
+ comp_func_solid_SourceIn_template<Rgba64Operations>(dest, length, color, const_alpha);
}
void QT_FASTCALL comp_func_SourceIn_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha)
{
comp_func_SourceIn_template<Rgba64Operations>(dest, src, length, const_alpha);
}
+#endif
/*
result = d * sa
@@ -665,20 +680,22 @@ void QT_FASTCALL comp_func_solid_DestinationIn(uint *dest, int length, uint colo
comp_func_solid_DestinationIn_template<Argb32Operations>(dest, length, color, const_alpha);
}
-void QT_FASTCALL comp_func_solid_DestinationIn_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
+void QT_FASTCALL comp_func_DestinationIn(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
{
- comp_func_solid_DestinationIn_template<Rgba64Operations>(dest, length, color, const_alpha);
+ comp_func_DestinationIn_template<Argb32Operations>(dest, src, length, const_alpha);
}
-void QT_FASTCALL comp_func_DestinationIn(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
+#if QT_CONFIG(raster_64bit)
+void QT_FASTCALL comp_func_solid_DestinationIn_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
{
- comp_func_DestinationIn_template<Argb32Operations>(dest, src, length, const_alpha);
+ comp_func_solid_DestinationIn_template<Rgba64Operations>(dest, length, color, const_alpha);
}
void QT_FASTCALL comp_func_DestinationIn_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha)
{
comp_func_DestinationIn_template<Rgba64Operations>(dest, src, length, const_alpha);
}
+#endif
/*
result = s * dia
@@ -727,20 +744,22 @@ void QT_FASTCALL comp_func_solid_SourceOut(uint *dest, int length, uint color, u
comp_func_solid_SourceOut_template<Argb32Operations>(dest, length, color, const_alpha);
}
-void QT_FASTCALL comp_func_solid_SourceOut_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
+void QT_FASTCALL comp_func_SourceOut(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
{
- comp_func_solid_SourceOut_template<Rgba64Operations>(dest, length, color, const_alpha);
+ comp_func_SourceOut_template<Argb32Operations>(dest, src, length, const_alpha);
}
-void QT_FASTCALL comp_func_SourceOut(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
+#if QT_CONFIG(raster_64bit)
+void QT_FASTCALL comp_func_solid_SourceOut_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
{
- comp_func_SourceOut_template<Argb32Operations>(dest, src, length, const_alpha);
+ comp_func_solid_SourceOut_template<Rgba64Operations>(dest, length, color, const_alpha);
}
void QT_FASTCALL comp_func_SourceOut_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha)
{
comp_func_SourceOut_template<Rgba64Operations>(dest, src, length, const_alpha);
}
+#endif
/*
result = d * sia
@@ -786,20 +805,22 @@ void QT_FASTCALL comp_func_solid_DestinationOut(uint *dest, int length, uint col
comp_func_solid_DestinationOut_template<Argb32Operations>(dest, length, color, const_alpha);
}
-void QT_FASTCALL comp_func_solid_DestinationOut_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
+void QT_FASTCALL comp_func_DestinationOut(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
{
- comp_func_solid_DestinationOut_template<Rgba64Operations>(dest, length, color, const_alpha);
+ comp_func_DestinationOut_template<Argb32Operations>(dest, src, length, const_alpha);
}
-void QT_FASTCALL comp_func_DestinationOut(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
+#if QT_CONFIG(raster_64bit)
+void QT_FASTCALL comp_func_solid_DestinationOut_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
{
- comp_func_DestinationOut_template<Argb32Operations>(dest, src, length, const_alpha);
+ comp_func_solid_DestinationOut_template<Rgba64Operations>(dest, length, color, const_alpha);
}
void QT_FASTCALL comp_func_DestinationOut_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha)
{
comp_func_DestinationOut_template<Rgba64Operations>(dest, src, length, const_alpha);
}
+#endif
/*
result = s*da + d*sia
@@ -845,20 +866,22 @@ void QT_FASTCALL comp_func_solid_SourceAtop(uint *dest, int length, uint color,
comp_func_solid_SourceAtop_template<Argb32Operations>(dest, length, color, const_alpha);
}
-void QT_FASTCALL comp_func_solid_SourceAtop_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
+void QT_FASTCALL comp_func_SourceAtop(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
{
- comp_func_solid_SourceAtop_template<Rgba64Operations>(dest, length, color, const_alpha);
+ comp_func_SourceAtop_template<Argb32Operations>(dest, src, length, const_alpha);
}
-void QT_FASTCALL comp_func_SourceAtop(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
+#if QT_CONFIG(raster_64bit)
+void QT_FASTCALL comp_func_solid_SourceAtop_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
{
- comp_func_SourceAtop_template<Argb32Operations>(dest, src, length, const_alpha);
+ comp_func_solid_SourceAtop_template<Rgba64Operations>(dest, length, color, const_alpha);
}
void QT_FASTCALL comp_func_SourceAtop_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha)
{
comp_func_SourceAtop_template<Rgba64Operations>(dest, src, length, const_alpha);
}
+#endif
/*
result = d*sa + s*dia
@@ -909,20 +932,22 @@ void QT_FASTCALL comp_func_solid_DestinationAtop(uint *dest, int length, uint co
comp_func_solid_DestinationAtop_template<Argb32Operations>(dest, length, color, const_alpha);
}
-void QT_FASTCALL comp_func_solid_DestinationAtop_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
+void QT_FASTCALL comp_func_DestinationAtop(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
{
- comp_func_solid_DestinationAtop_template<Rgba64Operations>(dest, length, color, const_alpha);
+ comp_func_DestinationAtop_template<Argb32Operations>(dest, src, length, const_alpha);
}
-void QT_FASTCALL comp_func_DestinationAtop(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
+#if QT_CONFIG(raster_64bit)
+void QT_FASTCALL comp_func_solid_DestinationAtop_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
{
- comp_func_DestinationAtop_template<Argb32Operations>(dest, src, length, const_alpha);
+ comp_func_solid_DestinationAtop_template<Rgba64Operations>(dest, length, color, const_alpha);
}
void QT_FASTCALL comp_func_DestinationAtop_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha)
{
comp_func_DestinationAtop_template<Rgba64Operations>(dest, src, length, const_alpha);
}
+#endif
/*
result = d*sia + s*dia
@@ -969,20 +994,22 @@ void QT_FASTCALL comp_func_solid_XOR(uint *dest, int length, uint color, uint co
comp_func_solid_XOR_template<Argb32Operations>(dest, length, color, const_alpha);
}
-void QT_FASTCALL comp_func_solid_XOR_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
+void QT_FASTCALL comp_func_XOR(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
{
- comp_func_solid_XOR_template<Rgba64Operations>(dest, length, color, const_alpha);
+ comp_func_XOR_template<Argb32Operations>(dest, src, length, const_alpha);
}
-void QT_FASTCALL comp_func_XOR(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
+#if QT_CONFIG(raster_64bit)
+void QT_FASTCALL comp_func_solid_XOR_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
{
- comp_func_XOR_template<Argb32Operations>(dest, src, length, const_alpha);
+ comp_func_solid_XOR_template<Rgba64Operations>(dest, length, color, const_alpha);
}
void QT_FASTCALL comp_func_XOR_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha)
{
comp_func_XOR_template<Rgba64Operations>(dest, src, length, const_alpha);
}
+#endif
struct QFullCoverage {
inline void store(uint *dest, const uint src) const
@@ -1078,20 +1105,22 @@ void QT_FASTCALL comp_func_solid_Plus(uint *dest, int length, uint color, uint c
comp_func_solid_Plus_template<Argb32Operations>(dest, length, color, const_alpha);
}
-void QT_FASTCALL comp_func_solid_Plus_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
+void QT_FASTCALL comp_func_Plus(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
{
- comp_func_solid_Plus_template<Rgba64Operations>(dest, length, color, const_alpha);
+ comp_func_Plus_template<Argb32Operations>(dest, src, length, const_alpha);
}
-void QT_FASTCALL comp_func_Plus(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
+#if QT_CONFIG(raster_64bit)
+void QT_FASTCALL comp_func_solid_Plus_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
{
- comp_func_Plus_template<Argb32Operations>(dest, src, length, const_alpha);
+ comp_func_solid_Plus_template<Rgba64Operations>(dest, length, color, const_alpha);
}
void QT_FASTCALL comp_func_Plus_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha)
{
comp_func_Plus_template<Rgba64Operations>(dest, src, length, const_alpha);
}
+#endif
/*
Dca' = Sca.Dca + Sca.(1 - Da) + Dca.(1 - Sa)
@@ -1101,11 +1130,6 @@ static inline int multiply_op(int dst, int src, int da, int sa)
return qt_div_255(src * dst + src * (255 - da) + dst * (255 - sa));
}
-static inline uint multiply_op_rgb64(uint dst, uint src, uint da, uint sa)
-{
- return qt_div_65535(src * dst + src * (65535 - da) + dst * (65535 - sa));
-}
-
template <typename T>
static inline void comp_func_solid_Multiply_impl(uint *dest, int length, uint color, const T &coverage)
{
@@ -1129,6 +1153,20 @@ static inline void comp_func_solid_Multiply_impl(uint *dest, int length, uint co
}
}
+void QT_FASTCALL comp_func_solid_Multiply(uint *dest, int length, uint color, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_solid_Multiply_impl(dest, length, color, QFullCoverage());
+ else
+ comp_func_solid_Multiply_impl(dest, length, color, QPartialCoverage(const_alpha));
+}
+
+#if QT_CONFIG(raster_64bit)
+static inline uint multiply_op_rgb64(uint dst, uint src, uint da, uint sa)
+{
+ return qt_div_65535(src * dst + src * (65535 - da) + dst * (65535 - sa));
+}
+
template <typename T>
static inline void comp_func_solid_Multiply_impl(QRgba64 *dest, int length, QRgba64 color, const T &coverage)
{
@@ -1152,14 +1190,6 @@ static inline void comp_func_solid_Multiply_impl(QRgba64 *dest, int length, QRgb
}
}
-void QT_FASTCALL comp_func_solid_Multiply(uint *dest, int length, uint color, uint const_alpha)
-{
- if (const_alpha == 255)
- comp_func_solid_Multiply_impl(dest, length, color, QFullCoverage());
- else
- comp_func_solid_Multiply_impl(dest, length, color, QPartialCoverage(const_alpha));
-}
-
void QT_FASTCALL comp_func_solid_Multiply_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
{
if (const_alpha == 255)
@@ -1167,6 +1197,7 @@ void QT_FASTCALL comp_func_solid_Multiply_rgb64(QRgba64 *dest, int length, QRgba
else
comp_func_solid_Multiply_impl(dest, length, color, QPartialCoverage(const_alpha));
}
+#endif
template <typename T>
static inline void comp_func_Multiply_impl(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, const T &coverage)
@@ -1189,6 +1220,15 @@ static inline void comp_func_Multiply_impl(uint *Q_DECL_RESTRICT dest, const uin
}
}
+void QT_FASTCALL comp_func_Multiply(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_Multiply_impl(dest, src, length, QFullCoverage());
+ else
+ comp_func_Multiply_impl(dest, src, length, QPartialCoverage(const_alpha));
+}
+
+#if QT_CONFIG(raster_64bit)
template <typename T>
static inline void comp_func_Multiply_impl(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, const T &coverage)
{
@@ -1210,14 +1250,6 @@ static inline void comp_func_Multiply_impl(QRgba64 *Q_DECL_RESTRICT dest, const
}
}
-void QT_FASTCALL comp_func_Multiply(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
-{
- if (const_alpha == 255)
- comp_func_Multiply_impl(dest, src, length, QFullCoverage());
- else
- comp_func_Multiply_impl(dest, src, length, QPartialCoverage(const_alpha));
-}
-
void QT_FASTCALL comp_func_Multiply_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha)
{
if (const_alpha == 255)
@@ -1225,6 +1257,7 @@ void QT_FASTCALL comp_func_Multiply_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const Q
else
comp_func_Multiply_impl(dest, src, length, QPartialCoverage(const_alpha));
}
+#endif
/*
Dca' = (Sca.Da + Dca.Sa - Sca.Dca) + Sca.(1 - Da) + Dca.(1 - Sa)
@@ -1253,6 +1286,15 @@ static inline void comp_func_solid_Screen_impl(uint *dest, int length, uint colo
}
}
+void QT_FASTCALL comp_func_solid_Screen(uint *dest, int length, uint color, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_solid_Screen_impl(dest, length, color, QFullCoverage());
+ else
+ comp_func_solid_Screen_impl(dest, length, color, QPartialCoverage(const_alpha));
+}
+
+#if QT_CONFIG(raster_64bit)
template <typename T>
static inline void comp_func_solid_Screen_impl(QRgba64 *dest, int length, QRgba64 color, const T &coverage)
{
@@ -1276,14 +1318,6 @@ static inline void comp_func_solid_Screen_impl(QRgba64 *dest, int length, QRgba6
}
}
-void QT_FASTCALL comp_func_solid_Screen(uint *dest, int length, uint color, uint const_alpha)
-{
- if (const_alpha == 255)
- comp_func_solid_Screen_impl(dest, length, color, QFullCoverage());
- else
- comp_func_solid_Screen_impl(dest, length, color, QPartialCoverage(const_alpha));
-}
-
void QT_FASTCALL comp_func_solid_Screen_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
{
if (const_alpha == 255)
@@ -1291,6 +1325,7 @@ void QT_FASTCALL comp_func_solid_Screen_rgb64(QRgba64 *dest, int length, QRgba64
else
comp_func_solid_Screen_impl(dest, length, color, QPartialCoverage(const_alpha));
}
+#endif
template <typename T>
static inline void comp_func_Screen_impl(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, const T &coverage)
@@ -1313,6 +1348,15 @@ static inline void comp_func_Screen_impl(uint *Q_DECL_RESTRICT dest, const uint
}
}
+void QT_FASTCALL comp_func_Screen(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_Screen_impl(dest, src, length, QFullCoverage());
+ else
+ comp_func_Screen_impl(dest, src, length, QPartialCoverage(const_alpha));
+}
+
+#if QT_CONFIG(raster_64bit)
template <typename T>
static inline void comp_func_Screen_impl(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, const T &coverage)
{
@@ -1334,14 +1378,6 @@ static inline void comp_func_Screen_impl(QRgba64 *Q_DECL_RESTRICT dest, const QR
}
}
-void QT_FASTCALL comp_func_Screen(uint *dest, const uint *src, int length, uint const_alpha)
-{
- if (const_alpha == 255)
- comp_func_Screen_impl(dest, src, length, QFullCoverage());
- else
- comp_func_Screen_impl(dest, src, length, QPartialCoverage(const_alpha));
-}
-
void QT_FASTCALL comp_func_Screen_rgb64(QRgba64 *dest, const QRgba64 *src, int length, uint const_alpha)
{
if (const_alpha == 255)
@@ -1349,6 +1385,7 @@ void QT_FASTCALL comp_func_Screen_rgb64(QRgba64 *dest, const QRgba64 *src, int l
else
comp_func_Screen_impl(dest, src, length, QPartialCoverage(const_alpha));
}
+#endif
/*
if 2.Dca < Da
@@ -1365,15 +1402,6 @@ static inline int overlay_op(int dst, int src, int da, int sa)
return qt_div_255(sa * da - 2 * (da - dst) * (sa - src) + temp);
}
-static inline uint overlay_op_rgb64(uint dst, uint src, uint da, uint sa)
-{
- const uint temp = src * (65535 - da) + dst * (65535 - sa);
- if (2 * dst < da)
- return qt_div_65535(2 * src * dst + temp);
- else
- return qt_div_65535(sa * da - 2 * (da - dst) * (sa - src) + temp);
-}
-
template <typename T>
static inline void comp_func_solid_Overlay_impl(uint *dest, int length, uint color, const T &coverage)
{
@@ -1397,6 +1425,24 @@ static inline void comp_func_solid_Overlay_impl(uint *dest, int length, uint col
}
}
+void QT_FASTCALL comp_func_solid_Overlay(uint *dest, int length, uint color, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_solid_Overlay_impl(dest, length, color, QFullCoverage());
+ else
+ comp_func_solid_Overlay_impl(dest, length, color, QPartialCoverage(const_alpha));
+}
+
+#if QT_CONFIG(raster_64bit)
+static inline uint overlay_op_rgb64(uint dst, uint src, uint da, uint sa)
+{
+ const uint temp = src * (65535 - da) + dst * (65535 - sa);
+ if (2 * dst < da)
+ return qt_div_65535(2 * src * dst + temp);
+ else
+ return qt_div_65535(sa * da - 2 * (da - dst) * (sa - src) + temp);
+}
+
template <typename T>
static inline void comp_func_solid_Overlay_impl(QRgba64 *dest, int length, QRgba64 color, const T &coverage)
{
@@ -1420,14 +1466,6 @@ static inline void comp_func_solid_Overlay_impl(QRgba64 *dest, int length, QRgba
}
}
-void QT_FASTCALL comp_func_solid_Overlay(uint *dest, int length, uint color, uint const_alpha)
-{
- if (const_alpha == 255)
- comp_func_solid_Overlay_impl(dest, length, color, QFullCoverage());
- else
- comp_func_solid_Overlay_impl(dest, length, color, QPartialCoverage(const_alpha));
-}
-
void QT_FASTCALL comp_func_solid_Overlay_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
{
if (const_alpha == 255)
@@ -1435,6 +1473,7 @@ void QT_FASTCALL comp_func_solid_Overlay_rgb64(QRgba64 *dest, int length, QRgba6
else
comp_func_solid_Overlay_impl(dest, length, color, QPartialCoverage(const_alpha));
}
+#endif
template <typename T>
static inline void comp_func_Overlay_impl(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, const T &coverage)
@@ -1457,6 +1496,15 @@ static inline void comp_func_Overlay_impl(uint *Q_DECL_RESTRICT dest, const uint
}
}
+void QT_FASTCALL comp_func_Overlay(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_Overlay_impl(dest, src, length, QFullCoverage());
+ else
+ comp_func_Overlay_impl(dest, src, length, QPartialCoverage(const_alpha));
+}
+
+#if QT_CONFIG(raster_64bit)
template <typename T>
static inline void comp_func_Overlay_impl(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, const T &coverage)
{
@@ -1478,14 +1526,6 @@ static inline void comp_func_Overlay_impl(QRgba64 *Q_DECL_RESTRICT dest, const Q
}
}
-void QT_FASTCALL comp_func_Overlay(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
-{
- if (const_alpha == 255)
- comp_func_Overlay_impl(dest, src, length, QFullCoverage());
- else
- comp_func_Overlay_impl(dest, src, length, QPartialCoverage(const_alpha));
-}
-
void QT_FASTCALL comp_func_Overlay_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha)
{
if (const_alpha == 255)
@@ -1493,6 +1533,7 @@ void QT_FASTCALL comp_func_Overlay_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QR
else
comp_func_Overlay_impl(dest, src, length, QPartialCoverage(const_alpha));
}
+#endif
/*
Dca' = min(Sca.Da, Dca.Sa) + Sca.(1 - Da) + Dca.(1 - Sa)
@@ -1503,11 +1544,6 @@ static inline int darken_op(int dst, int src, int da, int sa)
return qt_div_255(qMin(src * da, dst * sa) + src * (255 - da) + dst * (255 - sa));
}
-static inline uint darken_op_rgb64(uint dst, uint src, uint da, uint sa)
-{
- return qt_div_65535(qMin(src * da, dst * sa) + src * (65535 - da) + dst * (65535 - sa));
-}
-
template <typename T>
static inline void comp_func_solid_Darken_impl(uint *dest, int length, uint color, const T &coverage)
{
@@ -1531,6 +1567,20 @@ static inline void comp_func_solid_Darken_impl(uint *dest, int length, uint colo
}
}
+void QT_FASTCALL comp_func_solid_Darken(uint *dest, int length, uint color, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_solid_Darken_impl(dest, length, color, QFullCoverage());
+ else
+ comp_func_solid_Darken_impl(dest, length, color, QPartialCoverage(const_alpha));
+}
+
+#if QT_CONFIG(raster_64bit)
+static inline uint darken_op_rgb64(uint dst, uint src, uint da, uint sa)
+{
+ return qt_div_65535(qMin(src * da, dst * sa) + src * (65535 - da) + dst * (65535 - sa));
+}
+
template <typename T>
static inline void comp_func_solid_Darken_impl(QRgba64 *dest, int length, QRgba64 color, const T &coverage)
{
@@ -1554,14 +1604,6 @@ static inline void comp_func_solid_Darken_impl(QRgba64 *dest, int length, QRgba6
}
}
-void QT_FASTCALL comp_func_solid_Darken(uint *dest, int length, uint color, uint const_alpha)
-{
- if (const_alpha == 255)
- comp_func_solid_Darken_impl(dest, length, color, QFullCoverage());
- else
- comp_func_solid_Darken_impl(dest, length, color, QPartialCoverage(const_alpha));
-}
-
void QT_FASTCALL comp_func_solid_Darken_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
{
if (const_alpha == 255)
@@ -1569,6 +1611,7 @@ void QT_FASTCALL comp_func_solid_Darken_rgb64(QRgba64 *dest, int length, QRgba64
else
comp_func_solid_Darken_impl(dest, length, color, QPartialCoverage(const_alpha));
}
+#endif
template <typename T>
static inline void comp_func_Darken_impl(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, const T &coverage)
@@ -1591,6 +1634,15 @@ static inline void comp_func_Darken_impl(uint *Q_DECL_RESTRICT dest, const uint
}
}
+void QT_FASTCALL comp_func_Darken(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_Darken_impl(dest, src, length, QFullCoverage());
+ else
+ comp_func_Darken_impl(dest, src, length, QPartialCoverage(const_alpha));
+}
+
+#if QT_CONFIG(raster_64bit)
template <typename T>
static inline void comp_func_Darken_impl(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, const T &coverage)
{
@@ -1612,14 +1664,6 @@ static inline void comp_func_Darken_impl(QRgba64 *Q_DECL_RESTRICT dest, const QR
}
}
-void QT_FASTCALL comp_func_Darken(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
-{
- if (const_alpha == 255)
- comp_func_Darken_impl(dest, src, length, QFullCoverage());
- else
- comp_func_Darken_impl(dest, src, length, QPartialCoverage(const_alpha));
-}
-
void QT_FASTCALL comp_func_Darken_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha)
{
if (const_alpha == 255)
@@ -1627,6 +1671,7 @@ void QT_FASTCALL comp_func_Darken_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRg
else
comp_func_Darken_impl(dest, src, length, QPartialCoverage(const_alpha));
}
+#endif
/*
Dca' = max(Sca.Da, Dca.Sa) + Sca.(1 - Da) + Dca.(1 - Sa)
@@ -1637,11 +1682,6 @@ static inline int lighten_op(int dst, int src, int da, int sa)
return qt_div_255(qMax(src * da, dst * sa) + src * (255 - da) + dst * (255 - sa));
}
-static inline uint lighten_op_rgb64(uint dst, uint src, uint da, uint sa)
-{
- return qt_div_65535(qMax(src * da, dst * sa) + src * (65535 - da) + dst * (65535 - sa));
-}
-
template <typename T>
static inline void comp_func_solid_Lighten_impl(uint *dest, int length, uint color, const T &coverage)
{
@@ -1665,6 +1705,21 @@ static inline void comp_func_solid_Lighten_impl(uint *dest, int length, uint col
}
}
+void QT_FASTCALL comp_func_solid_Lighten(uint *dest, int length, uint color, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_solid_Lighten_impl(dest, length, color, QFullCoverage());
+ else
+ comp_func_solid_Lighten_impl(dest, length, color, QPartialCoverage(const_alpha));
+}
+
+
+#if QT_CONFIG(raster_64bit)
+static inline uint lighten_op_rgb64(uint dst, uint src, uint da, uint sa)
+{
+ return qt_div_65535(qMax(src * da, dst * sa) + src * (65535 - da) + dst * (65535 - sa));
+}
+
template <typename T>
static inline void comp_func_solid_Lighten_impl(QRgba64 *dest, int length, QRgba64 color, const T &coverage)
{
@@ -1688,14 +1743,6 @@ static inline void comp_func_solid_Lighten_impl(QRgba64 *dest, int length, QRgba
}
}
-void QT_FASTCALL comp_func_solid_Lighten(uint *dest, int length, uint color, uint const_alpha)
-{
- if (const_alpha == 255)
- comp_func_solid_Lighten_impl(dest, length, color, QFullCoverage());
- else
- comp_func_solid_Lighten_impl(dest, length, color, QPartialCoverage(const_alpha));
-}
-
void QT_FASTCALL comp_func_solid_Lighten_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
{
if (const_alpha == 255)
@@ -1703,6 +1750,7 @@ void QT_FASTCALL comp_func_solid_Lighten_rgb64(QRgba64 *dest, int length, QRgba6
else
comp_func_solid_Lighten_impl(dest, length, color, QPartialCoverage(const_alpha));
}
+#endif
template <typename T>
static inline void comp_func_Lighten_impl(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, const T &coverage)
@@ -1725,6 +1773,15 @@ static inline void comp_func_Lighten_impl(uint *Q_DECL_RESTRICT dest, const uint
}
}
+void QT_FASTCALL comp_func_Lighten(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_Lighten_impl(dest, src, length, QFullCoverage());
+ else
+ comp_func_Lighten_impl(dest, src, length, QPartialCoverage(const_alpha));
+}
+
+#if QT_CONFIG(raster_64bit)
template <typename T>
static inline void comp_func_Lighten_impl(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, const T &coverage)
{
@@ -1746,14 +1803,6 @@ static inline void comp_func_Lighten_impl(QRgba64 *Q_DECL_RESTRICT dest, const Q
}
}
-void QT_FASTCALL comp_func_Lighten(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
-{
- if (const_alpha == 255)
- comp_func_Lighten_impl(dest, src, length, QFullCoverage());
- else
- comp_func_Lighten_impl(dest, src, length, QPartialCoverage(const_alpha));
-}
-
void QT_FASTCALL comp_func_Lighten_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha)
{
if (const_alpha == 255)
@@ -1761,6 +1810,7 @@ void QT_FASTCALL comp_func_Lighten_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QR
else
comp_func_Lighten_impl(dest, src, length, QPartialCoverage(const_alpha));
}
+#endif
/*
if Sca.Da + Dca.Sa >= Sa.Da
@@ -1781,19 +1831,6 @@ static inline int color_dodge_op(int dst, int src, int da, int sa)
return qt_div_255(255 * dst_sa / (255 - 255 * src / sa) + temp);
}
-static inline uint color_dodge_op_rgb64(qint64 dst, qint64 src, qint64 da, qint64 sa)
-{
- const qint64 sa_da = sa * da;
- const qint64 dst_sa = dst * sa;
- const qint64 src_da = src * da;
-
- const qint64 temp = src * (65535 - da) + dst * (65535 - sa);
- if (src_da + dst_sa >= sa_da)
- return qt_div_65535(sa_da + temp);
- else
- return qt_div_65535(65535 * dst_sa / (65535 - 65535 * src / sa) + temp);
-}
-
template <typename T>
static inline void comp_func_solid_ColorDodge_impl(uint *dest, int length, uint color, const T &coverage)
{
@@ -1817,6 +1854,28 @@ static inline void comp_func_solid_ColorDodge_impl(uint *dest, int length, uint
}
}
+void QT_FASTCALL comp_func_solid_ColorDodge(uint *dest, int length, uint color, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_solid_ColorDodge_impl(dest, length, color, QFullCoverage());
+ else
+ comp_func_solid_ColorDodge_impl(dest, length, color, QPartialCoverage(const_alpha));
+}
+
+#if QT_CONFIG(raster_64bit)
+static inline uint color_dodge_op_rgb64(qint64 dst, qint64 src, qint64 da, qint64 sa)
+{
+ const qint64 sa_da = sa * da;
+ const qint64 dst_sa = dst * sa;
+ const qint64 src_da = src * da;
+
+ const qint64 temp = src * (65535 - da) + dst * (65535 - sa);
+ if (src_da + dst_sa >= sa_da)
+ return qt_div_65535(sa_da + temp);
+ else
+ return qt_div_65535(65535 * dst_sa / (65535 - 65535 * src / sa) + temp);
+}
+
template <typename T>
static inline void comp_func_solid_ColorDodge_impl(QRgba64 *dest, int length, QRgba64 color, const T &coverage)
{
@@ -1840,14 +1899,6 @@ static inline void comp_func_solid_ColorDodge_impl(QRgba64 *dest, int length, QR
}
}
-void QT_FASTCALL comp_func_solid_ColorDodge(uint *dest, int length, uint color, uint const_alpha)
-{
- if (const_alpha == 255)
- comp_func_solid_ColorDodge_impl(dest, length, color, QFullCoverage());
- else
- comp_func_solid_ColorDodge_impl(dest, length, color, QPartialCoverage(const_alpha));
-}
-
void QT_FASTCALL comp_func_solid_ColorDodge_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
{
if (const_alpha == 255)
@@ -1855,6 +1906,7 @@ void QT_FASTCALL comp_func_solid_ColorDodge_rgb64(QRgba64 *dest, int length, QRg
else
comp_func_solid_ColorDodge_impl(dest, length, color, QPartialCoverage(const_alpha));
}
+#endif
template <typename T>
static inline void comp_func_ColorDodge_impl(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, const T &coverage)
@@ -1877,6 +1929,15 @@ static inline void comp_func_ColorDodge_impl(uint *Q_DECL_RESTRICT dest, const u
}
}
+void QT_FASTCALL comp_func_ColorDodge(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_ColorDodge_impl(dest, src, length, QFullCoverage());
+ else
+ comp_func_ColorDodge_impl(dest, src, length, QPartialCoverage(const_alpha));
+}
+
+#if QT_CONFIG(raster_64bit)
template <typename T>
static inline void comp_func_ColorDodge_impl(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, const T &coverage)
{
@@ -1898,14 +1959,6 @@ static inline void comp_func_ColorDodge_impl(QRgba64 *Q_DECL_RESTRICT dest, cons
}
}
-void QT_FASTCALL comp_func_ColorDodge(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
-{
- if (const_alpha == 255)
- comp_func_ColorDodge_impl(dest, src, length, QFullCoverage());
- else
- comp_func_ColorDodge_impl(dest, src, length, QPartialCoverage(const_alpha));
-}
-
void QT_FASTCALL comp_func_ColorDodge_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha)
{
if (const_alpha == 255)
@@ -1913,6 +1966,7 @@ void QT_FASTCALL comp_func_ColorDodge_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const
else
comp_func_ColorDodge_impl(dest, src, length, QPartialCoverage(const_alpha));
}
+#endif
/*
if Sca.Da + Dca.Sa <= Sa.Da
@@ -1933,19 +1987,6 @@ static inline int color_burn_op(int dst, int src, int da, int sa)
return qt_div_255(sa * (src_da + dst_sa - sa_da) / src + temp);
}
-static inline uint color_burn_op_rgb64(qint64 dst, qint64 src, qint64 da, qint64 sa)
-{
- const qint64 src_da = src * da;
- const qint64 dst_sa = dst * sa;
- const qint64 sa_da = sa * da;
-
- const qint64 temp = src * (65535 - da) + dst * (65535 - sa);
-
- if (src == 0 || src_da + dst_sa <= sa_da)
- return qt_div_65535(temp);
- return qt_div_65535(sa * (src_da + dst_sa - sa_da) / src + temp);
-}
-
template <typename T>
static inline void comp_func_solid_ColorBurn_impl(uint *dest, int length, uint color, const T &coverage)
{
@@ -1969,6 +2010,28 @@ static inline void comp_func_solid_ColorBurn_impl(uint *dest, int length, uint c
}
}
+void QT_FASTCALL comp_func_solid_ColorBurn(uint *dest, int length, uint color, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_solid_ColorBurn_impl(dest, length, color, QFullCoverage());
+ else
+ comp_func_solid_ColorBurn_impl(dest, length, color, QPartialCoverage(const_alpha));
+}
+
+#if QT_CONFIG(raster_64bit)
+static inline uint color_burn_op_rgb64(qint64 dst, qint64 src, qint64 da, qint64 sa)
+{
+ const qint64 src_da = src * da;
+ const qint64 dst_sa = dst * sa;
+ const qint64 sa_da = sa * da;
+
+ const qint64 temp = src * (65535 - da) + dst * (65535 - sa);
+
+ if (src == 0 || src_da + dst_sa <= sa_da)
+ return qt_div_65535(temp);
+ return qt_div_65535(sa * (src_da + dst_sa - sa_da) / src + temp);
+}
+
template <typename T>
static inline void comp_func_solid_ColorBurn_impl(QRgba64 *dest, int length, QRgba64 color, const T &coverage)
{
@@ -1992,14 +2055,6 @@ static inline void comp_func_solid_ColorBurn_impl(QRgba64 *dest, int length, QRg
}
}
-void QT_FASTCALL comp_func_solid_ColorBurn(uint *dest, int length, uint color, uint const_alpha)
-{
- if (const_alpha == 255)
- comp_func_solid_ColorBurn_impl(dest, length, color, QFullCoverage());
- else
- comp_func_solid_ColorBurn_impl(dest, length, color, QPartialCoverage(const_alpha));
-}
-
void QT_FASTCALL comp_func_solid_ColorBurn_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
{
if (const_alpha == 255)
@@ -2007,6 +2062,7 @@ void QT_FASTCALL comp_func_solid_ColorBurn_rgb64(QRgba64 *dest, int length, QRgb
else
comp_func_solid_ColorBurn_impl(dest, length, color, QPartialCoverage(const_alpha));
}
+#endif
template <typename T>
static inline void comp_func_ColorBurn_impl(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, const T &coverage)
@@ -2029,6 +2085,15 @@ static inline void comp_func_ColorBurn_impl(uint *Q_DECL_RESTRICT dest, const ui
}
}
+void QT_FASTCALL comp_func_ColorBurn(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_ColorBurn_impl(dest, src, length, QFullCoverage());
+ else
+ comp_func_ColorBurn_impl(dest, src, length, QPartialCoverage(const_alpha));
+}
+
+#if QT_CONFIG(raster_64bit)
template <typename T>
static inline void comp_func_ColorBurn_impl(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, const T &coverage)
{
@@ -2050,14 +2115,6 @@ static inline void comp_func_ColorBurn_impl(QRgba64 *Q_DECL_RESTRICT dest, const
}
}
-void QT_FASTCALL comp_func_ColorBurn(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
-{
- if (const_alpha == 255)
- comp_func_ColorBurn_impl(dest, src, length, QFullCoverage());
- else
- comp_func_ColorBurn_impl(dest, src, length, QPartialCoverage(const_alpha));
-}
-
void QT_FASTCALL comp_func_ColorBurn_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha)
{
if (const_alpha == 255)
@@ -2065,6 +2122,7 @@ void QT_FASTCALL comp_func_ColorBurn_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const
else
comp_func_ColorBurn_impl(dest, src, length, QPartialCoverage(const_alpha));
}
+#endif
/*
if 2.Sca < Sa
@@ -2082,16 +2140,6 @@ static inline uint hardlight_op(int dst, int src, int da, int sa)
return qt_div_255(sa * da - 2 * (da - dst) * (sa - src) + temp);
}
-static inline uint hardlight_op_rgb64(uint dst, uint src, uint da, uint sa)
-{
- const uint temp = src * (65535 - da) + dst * (65535 - sa);
-
- if (2 * src < sa)
- return qt_div_65535(2 * src * dst + temp);
- else
- return qt_div_65535(sa * da - 2 * (da - dst) * (sa - src) + temp);
-}
-
template <typename T>
static inline void comp_func_solid_HardLight_impl(uint *dest, int length, uint color, const T &coverage)
{
@@ -2115,6 +2163,25 @@ static inline void comp_func_solid_HardLight_impl(uint *dest, int length, uint c
}
}
+void QT_FASTCALL comp_func_solid_HardLight(uint *dest, int length, uint color, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_solid_HardLight_impl(dest, length, color, QFullCoverage());
+ else
+ comp_func_solid_HardLight_impl(dest, length, color, QPartialCoverage(const_alpha));
+}
+
+#if QT_CONFIG(raster_64bit)
+static inline uint hardlight_op_rgb64(uint dst, uint src, uint da, uint sa)
+{
+ const uint temp = src * (65535 - da) + dst * (65535 - sa);
+
+ if (2 * src < sa)
+ return qt_div_65535(2 * src * dst + temp);
+ else
+ return qt_div_65535(sa * da - 2 * (da - dst) * (sa - src) + temp);
+}
+
template <typename T>
static inline void comp_func_solid_HardLight_impl(QRgba64 *dest, int length, QRgba64 color, const T &coverage)
{
@@ -2138,14 +2205,6 @@ static inline void comp_func_solid_HardLight_impl(QRgba64 *dest, int length, QRg
}
}
-void QT_FASTCALL comp_func_solid_HardLight(uint *dest, int length, uint color, uint const_alpha)
-{
- if (const_alpha == 255)
- comp_func_solid_HardLight_impl(dest, length, color, QFullCoverage());
- else
- comp_func_solid_HardLight_impl(dest, length, color, QPartialCoverage(const_alpha));
-}
-
void QT_FASTCALL comp_func_solid_HardLight_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
{
if (const_alpha == 255)
@@ -2153,6 +2212,7 @@ void QT_FASTCALL comp_func_solid_HardLight_rgb64(QRgba64 *dest, int length, QRgb
else
comp_func_solid_HardLight_impl(dest, length, color, QPartialCoverage(const_alpha));
}
+#endif
template <typename T>
static inline void comp_func_HardLight_impl(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, const T &coverage)
@@ -2175,6 +2235,15 @@ static inline void comp_func_HardLight_impl(uint *Q_DECL_RESTRICT dest, const ui
}
}
+void QT_FASTCALL comp_func_HardLight(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_HardLight_impl(dest, src, length, QFullCoverage());
+ else
+ comp_func_HardLight_impl(dest, src, length, QPartialCoverage(const_alpha));
+}
+
+#if QT_CONFIG(raster_64bit)
template <typename T>
static inline void comp_func_HardLight_impl(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, const T &coverage)
{
@@ -2196,14 +2265,6 @@ static inline void comp_func_HardLight_impl(QRgba64 *Q_DECL_RESTRICT dest, const
}
}
-void QT_FASTCALL comp_func_HardLight(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
-{
- if (const_alpha == 255)
- comp_func_HardLight_impl(dest, src, length, QFullCoverage());
- else
- comp_func_HardLight_impl(dest, src, length, QPartialCoverage(const_alpha));
-}
-
void QT_FASTCALL comp_func_HardLight_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha)
{
if (const_alpha == 255)
@@ -2211,6 +2272,7 @@ void QT_FASTCALL comp_func_HardLight_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const
else
comp_func_HardLight_impl(dest, src, length, QPartialCoverage(const_alpha));
}
+#endif
/*
if 2.Sca <= Sa
@@ -2235,22 +2297,6 @@ static inline int soft_light_op(int dst, int src, int da, int sa)
}
}
-static inline uint soft_light_op_rgb64(qint64 dst, qint64 src, qint64 da, qint64 sa)
-{
- const qint64 src2 = src << 1;
- const qint64 dst_np = da != 0 ? (65535 * dst) / da : 0;
- const qint64 temp = (src * (65535 - da) + dst * (65535 - sa)) * 65535;
- const qint64 factor = qint64(65535) * 65535;
-
- if (src2 < sa)
- return (dst * (sa * 65535 + (src2 - sa) * (65535 - dst_np)) + temp) / factor;
- else if (4 * dst <= da)
- return (dst * sa * 65535 + da * (src2 - sa) * ((((16 * dst_np - 12 * 65535) * dst_np + 3 * factor) * dst_np) / factor) + temp) / factor;
- else {
- return (dst * sa * 65535 + da * (src2 - sa) * (int(qSqrt(qreal(dst_np * 65535))) - dst_np) + temp) / factor;
- }
-}
-
template <typename T>
static inline void comp_func_solid_SoftLight_impl(uint *dest, int length, uint color, const T &coverage)
{
@@ -2274,6 +2320,23 @@ static inline void comp_func_solid_SoftLight_impl(uint *dest, int length, uint c
}
}
+#if QT_CONFIG(raster_64bit)
+static inline uint soft_light_op_rgb64(qint64 dst, qint64 src, qint64 da, qint64 sa)
+{
+ const qint64 src2 = src << 1;
+ const qint64 dst_np = da != 0 ? (65535 * dst) / da : 0;
+ const qint64 temp = (src * (65535 - da) + dst * (65535 - sa)) * 65535;
+ const qint64 factor = qint64(65535) * 65535;
+
+ if (src2 < sa)
+ return (dst * (sa * 65535 + (src2 - sa) * (65535 - dst_np)) + temp) / factor;
+ else if (4 * dst <= da)
+ return (dst * sa * 65535 + da * (src2 - sa) * ((((16 * dst_np - 12 * 65535) * dst_np + 3 * factor) * dst_np) / factor) + temp) / factor;
+ else {
+ return (dst * sa * 65535 + da * (src2 - sa) * (int(qSqrt(qreal(dst_np * 65535))) - dst_np) + temp) / factor;
+ }
+}
+
template <typename T>
static inline void comp_func_solid_SoftLight_impl(QRgba64 *dest, int length, QRgba64 color, const T &coverage)
{
@@ -2296,6 +2359,7 @@ static inline void comp_func_solid_SoftLight_impl(QRgba64 *dest, int length, QRg
coverage.store(&dest[i], qRgba64(r, g, b, a));
}
}
+#endif
void QT_FASTCALL comp_func_solid_SoftLight(uint *dest, int length, uint color, uint const_alpha)
{
@@ -2305,14 +2369,6 @@ void QT_FASTCALL comp_func_solid_SoftLight(uint *dest, int length, uint color, u
comp_func_solid_SoftLight_impl(dest, length, color, QPartialCoverage(const_alpha));
}
-void QT_FASTCALL comp_func_solid_SoftLight_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
-{
- if (const_alpha == 255)
- comp_func_solid_SoftLight_impl(dest, length, color, QFullCoverage());
- else
- comp_func_solid_SoftLight_impl(dest, length, color, QPartialCoverage(const_alpha));
-}
-
template <typename T>
static inline void comp_func_SoftLight_impl(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, const T &coverage)
{
@@ -2334,6 +2390,23 @@ static inline void comp_func_SoftLight_impl(uint *Q_DECL_RESTRICT dest, const ui
}
}
+void QT_FASTCALL comp_func_SoftLight(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_SoftLight_impl(dest, src, length, QFullCoverage());
+ else
+ comp_func_SoftLight_impl(dest, src, length, QPartialCoverage(const_alpha));
+}
+
+#if QT_CONFIG(raster_64bit)
+void QT_FASTCALL comp_func_solid_SoftLight_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_solid_SoftLight_impl(dest, length, color, QFullCoverage());
+ else
+ comp_func_solid_SoftLight_impl(dest, length, color, QPartialCoverage(const_alpha));
+}
+
template <typename T>
static inline void comp_func_SoftLight_impl(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, const T &coverage)
{
@@ -2355,14 +2428,6 @@ static inline void comp_func_SoftLight_impl(QRgba64 *Q_DECL_RESTRICT dest, const
}
}
-void QT_FASTCALL comp_func_SoftLight(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
-{
- if (const_alpha == 255)
- comp_func_SoftLight_impl(dest, src, length, QFullCoverage());
- else
- comp_func_SoftLight_impl(dest, src, length, QPartialCoverage(const_alpha));
-}
-
void QT_FASTCALL comp_func_SoftLight_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha)
{
if (const_alpha == 255)
@@ -2370,6 +2435,7 @@ void QT_FASTCALL comp_func_SoftLight_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const
else
comp_func_SoftLight_impl(dest, src, length, QPartialCoverage(const_alpha));
}
+#endif
/*
Dca' = abs(Dca.Sa - Sca.Da) + Sca.(1 - Da) + Dca.(1 - Sa)
@@ -2380,11 +2446,6 @@ static inline int difference_op(int dst, int src, int da, int sa)
return src + dst - qt_div_255(2 * qMin(src * da, dst * sa));
}
-static inline uint difference_op_rgb64(qint64 dst, qint64 src, qint64 da, qint64 sa)
-{
- return src + dst - qt_div_65535(2 * qMin(src * da, dst * sa));
-}
-
template <typename T>
static inline void comp_func_solid_Difference_impl(uint *dest, int length, uint color, const T &coverage)
{
@@ -2408,6 +2469,20 @@ static inline void comp_func_solid_Difference_impl(uint *dest, int length, uint
}
}
+void QT_FASTCALL comp_func_solid_Difference(uint *dest, int length, uint color, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_solid_Difference_impl(dest, length, color, QFullCoverage());
+ else
+ comp_func_solid_Difference_impl(dest, length, color, QPartialCoverage(const_alpha));
+}
+
+#if QT_CONFIG(raster_64bit)
+static inline uint difference_op_rgb64(qint64 dst, qint64 src, qint64 da, qint64 sa)
+{
+ return src + dst - qt_div_65535(2 * qMin(src * da, dst * sa));
+}
+
template <typename T>
static inline void comp_func_solid_Difference_impl(QRgba64 *dest, int length, QRgba64 color, const T &coverage)
{
@@ -2431,14 +2506,6 @@ static inline void comp_func_solid_Difference_impl(QRgba64 *dest, int length, QR
}
}
-void QT_FASTCALL comp_func_solid_Difference(uint *dest, int length, uint color, uint const_alpha)
-{
- if (const_alpha == 255)
- comp_func_solid_Difference_impl(dest, length, color, QFullCoverage());
- else
- comp_func_solid_Difference_impl(dest, length, color, QPartialCoverage(const_alpha));
-}
-
void QT_FASTCALL comp_func_solid_Difference_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
{
if (const_alpha == 255)
@@ -2446,6 +2513,7 @@ void QT_FASTCALL comp_func_solid_Difference_rgb64(QRgba64 *dest, int length, QRg
else
comp_func_solid_Difference_impl(dest, length, color, QPartialCoverage(const_alpha));
}
+#endif
template <typename T>
static inline void comp_func_Difference_impl(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, const T &coverage)
@@ -2468,6 +2536,15 @@ static inline void comp_func_Difference_impl(uint *Q_DECL_RESTRICT dest, const u
}
}
+void QT_FASTCALL comp_func_Difference(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_Difference_impl(dest, src, length, QFullCoverage());
+ else
+ comp_func_Difference_impl(dest, src, length, QPartialCoverage(const_alpha));
+}
+
+#if QT_CONFIG(raster_64bit)
template <typename T>
static inline void comp_func_Difference_impl(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, const T &coverage)
{
@@ -2489,14 +2566,6 @@ static inline void comp_func_Difference_impl(QRgba64 *Q_DECL_RESTRICT dest, cons
}
}
-void QT_FASTCALL comp_func_Difference(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
-{
- if (const_alpha == 255)
- comp_func_Difference_impl(dest, src, length, QFullCoverage());
- else
- comp_func_Difference_impl(dest, src, length, QPartialCoverage(const_alpha));
-}
-
void QT_FASTCALL comp_func_Difference_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha)
{
if (const_alpha == 255)
@@ -2504,6 +2573,7 @@ void QT_FASTCALL comp_func_Difference_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const
else
comp_func_Difference_impl(dest, src, length, QPartialCoverage(const_alpha));
}
+#endif
/*
Dca' = (Sca.Da + Dca.Sa - 2.Sca.Dca) + Sca.(1 - Da) + Dca.(1 - Sa)
@@ -2531,6 +2601,15 @@ static inline void QT_FASTCALL comp_func_solid_Exclusion_impl(uint *dest, int le
}
}
+void QT_FASTCALL comp_func_solid_Exclusion(uint *dest, int length, uint color, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_solid_Exclusion_impl(dest, length, color, QFullCoverage());
+ else
+ comp_func_solid_Exclusion_impl(dest, length, color, QPartialCoverage(const_alpha));
+}
+
+#if QT_CONFIG(raster_64bit)
template <typename T>
static inline void QT_FASTCALL comp_func_solid_Exclusion_impl(QRgba64 *dest, int length, QRgba64 color, const T &coverage)
{
@@ -2555,14 +2634,6 @@ static inline void QT_FASTCALL comp_func_solid_Exclusion_impl(QRgba64 *dest, int
}
-void QT_FASTCALL comp_func_solid_Exclusion(uint *dest, int length, uint color, uint const_alpha)
-{
- if (const_alpha == 255)
- comp_func_solid_Exclusion_impl(dest, length, color, QFullCoverage());
- else
- comp_func_solid_Exclusion_impl(dest, length, color, QPartialCoverage(const_alpha));
-}
-
void QT_FASTCALL comp_func_solid_Exclusion_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
{
if (const_alpha == 255)
@@ -2570,6 +2641,7 @@ void QT_FASTCALL comp_func_solid_Exclusion_rgb64(QRgba64 *dest, int length, QRgb
else
comp_func_solid_Exclusion_impl(dest, length, color, QPartialCoverage(const_alpha));
}
+#endif
template <typename T>
static inline void comp_func_Exclusion_impl(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, const T &coverage)
@@ -2592,6 +2664,15 @@ static inline void comp_func_Exclusion_impl(uint *Q_DECL_RESTRICT dest, const ui
}
}
+void QT_FASTCALL comp_func_Exclusion(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_Exclusion_impl(dest, src, length, QFullCoverage());
+ else
+ comp_func_Exclusion_impl(dest, src, length, QPartialCoverage(const_alpha));
+}
+
+#if QT_CONFIG(raster_64bit)
template <typename T>
static inline void comp_func_Exclusion_impl(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, const T &coverage)
{
@@ -2613,14 +2694,6 @@ static inline void comp_func_Exclusion_impl(QRgba64 *Q_DECL_RESTRICT dest, const
}
}
-void QT_FASTCALL comp_func_Exclusion(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
-{
- if (const_alpha == 255)
- comp_func_Exclusion_impl(dest, src, length, QFullCoverage());
- else
- comp_func_Exclusion_impl(dest, src, length, QPartialCoverage(const_alpha));
-}
-
void QT_FASTCALL comp_func_Exclusion_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha)
{
if (const_alpha == 255)
@@ -2628,6 +2701,7 @@ void QT_FASTCALL comp_func_Exclusion_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const
else
comp_func_Exclusion_impl(dest, src, length, QPartialCoverage(const_alpha));
}
+#endif
void QT_FASTCALL rasterop_solid_SourceOrDestination(uint *dest,
int length,
@@ -2977,6 +3051,7 @@ CompositionFunctionSolid qt_functionForModeSolid_C[] = {
};
CompositionFunctionSolid64 qt_functionForModeSolid64_C[] = {
+#if QT_CONFIG(raster_64bit)
comp_func_solid_SourceOver_rgb64,
comp_func_solid_DestinationOver_rgb64,
comp_func_solid_Clear_rgb64,
@@ -3001,6 +3076,10 @@ CompositionFunctionSolid64 qt_functionForModeSolid64_C[] = {
comp_func_solid_SoftLight_rgb64,
comp_func_solid_Difference_rgb64,
comp_func_solid_Exclusion_rgb64,
+#else
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+#endif
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0
};
@@ -3047,6 +3126,7 @@ CompositionFunction qt_functionForMode_C[] = {
};
CompositionFunction64 qt_functionForMode64_C[] = {
+#if QT_CONFIG(raster_64bit)
comp_func_SourceOver_rgb64,
comp_func_DestinationOver_rgb64,
comp_func_Clear_rgb64,
@@ -3071,6 +3151,10 @@ CompositionFunction64 qt_functionForMode64_C[] = {
comp_func_SoftLight_rgb64,
comp_func_Difference_rgb64,
comp_func_Exclusion_rgb64,
+#else
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+#endif
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0
};
diff --git a/src/gui/painting/qcosmeticstroker_p.h b/src/gui/painting/qcosmeticstroker_p.h
index 082ddee30f..8571b0476a 100644
--- a/src/gui/painting/qcosmeticstroker_p.h
+++ b/src/gui/painting/qcosmeticstroker_p.h
@@ -98,8 +98,8 @@ public:
: state(s),
deviceRect(dr_unclipped),
clip(dr),
- pattern(0),
- reversePattern(0),
+ pattern(nullptr),
+ reversePattern(nullptr),
patternSize(0),
patternLength(0),
patternOffset(0),
diff --git a/src/gui/painting/qdatabuffer_p.h b/src/gui/painting/qdatabuffer_p.h
index 28d5f6d6c5..181d19da0b 100644
--- a/src/gui/painting/qdatabuffer_p.h
+++ b/src/gui/painting/qdatabuffer_p.h
@@ -69,7 +69,7 @@ public:
buffer = (Type*) malloc(capacity * sizeof(Type));
Q_CHECK_PTR(buffer);
} else {
- buffer = 0;
+ buffer = nullptr;
}
siz = 0;
}
@@ -128,7 +128,7 @@ public:
Q_CHECK_PTR(buffer);
} else {
free(buffer);
- buffer = 0;
+ buffer = nullptr;
}
}
diff --git a/src/gui/painting/qdrawhelper.cpp b/src/gui/painting/qdrawhelper.cpp
index 2dd18f6dfc..ac15d8f4f4 100644
--- a/src/gui/painting/qdrawhelper.cpp
+++ b/src/gui/painting/qdrawhelper.cpp
@@ -43,7 +43,7 @@
#include <qstylehints.h>
#include <qguiapplication.h>
#include <qatomic.h>
-#include <private/qcolorprofile_p.h>
+#include <private/qcolortrclut_p.h>
#include <private/qdrawhelper_p.h>
#include <private/qpaintengine_raster_p.h>
#include <private/qpainter_p.h>
@@ -1117,6 +1117,7 @@ static const QRgba64 *QT_FASTCALL fetchARGB32PMToRGBA64PM(QRgba64 *buffer, const
return convertARGB32PMToRGBA64PM(buffer, reinterpret_cast<const uint *>(src) + index, count, nullptr, nullptr);
}
+#if QT_CONFIG(raster_64bit)
static void convertRGBA64ToRGBA64PM(QRgba64 *buffer, int count)
{
for (int i = 0; i < count; ++i)
@@ -1126,6 +1127,7 @@ static void convertRGBA64ToRGBA64PM(QRgba64 *buffer, int count)
static void convertRGBA64PMToRGBA64PM(QRgba64 *, int)
{
}
+#endif
static const QRgba64 *QT_FASTCALL fetchRGBA64ToRGBA64PM(QRgba64 *buffer, const uchar *src, int index, int count,
const QVector<QRgb> *, QDitherInfo *)
@@ -1699,22 +1701,6 @@ static uint *QT_FASTCALL destFetchUndefined(uint *buffer, QRasterBuffer *, int,
return buffer;
}
-static QRgba64 *QT_FASTCALL destFetch64(QRgba64 *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length)
-{
- const QPixelLayout *layout = &qPixelLayouts[rasterBuffer->format];
- return const_cast<QRgba64 *>(layout->fetchToRGBA64PM(buffer, rasterBuffer->scanLine(y), x, length, nullptr, nullptr));
-}
-
-static QRgba64 * QT_FASTCALL destFetchRGB64(QRgba64 *, QRasterBuffer *rasterBuffer, int x, int y, int)
-{
- return (QRgba64 *)rasterBuffer->scanLine(y) + x;
-}
-
-static QRgba64 * QT_FASTCALL destFetch64Undefined(QRgba64 *buffer, QRasterBuffer *, int, int, int)
-{
- return buffer;
-}
-
static DestFetchProc destFetchProc[QImage::NImageFormats] =
{
0, // Format_Invalid
@@ -1748,6 +1734,23 @@ static DestFetchProc destFetchProc[QImage::NImageFormats] =
destFetch, // Format_Grayscale16
};
+#if QT_CONFIG(raster_64bit)
+static QRgba64 *QT_FASTCALL destFetch64(QRgba64 *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length)
+{
+ const QPixelLayout *layout = &qPixelLayouts[rasterBuffer->format];
+ return const_cast<QRgba64 *>(layout->fetchToRGBA64PM(buffer, rasterBuffer->scanLine(y), x, length, nullptr, nullptr));
+}
+
+static QRgba64 * QT_FASTCALL destFetchRGB64(QRgba64 *, QRasterBuffer *rasterBuffer, int x, int y, int)
+{
+ return (QRgba64 *)rasterBuffer->scanLine(y) + x;
+}
+
+static QRgba64 * QT_FASTCALL destFetch64Undefined(QRgba64 *buffer, QRasterBuffer *, int, int, int)
+{
+ return buffer;
+}
+
static DestFetchProc64 destFetchProc64[QImage::NImageFormats] =
{
0, // Format_Invalid
@@ -1780,6 +1783,7 @@ static DestFetchProc64 destFetchProc64[QImage::NImageFormats] =
destFetchRGB64, // Format_RGBA64_Premultiplied
destFetch64, // Format_Grayscale16
};
+#endif
/*
Returns the color in the mono destination color table
@@ -1887,21 +1891,6 @@ static void QT_FASTCALL destStore(QRasterBuffer *rasterBuffer, int x, int y, con
store(dest, buffer, x, length, nullptr, nullptr);
}
-static void QT_FASTCALL destStore64(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length)
-{
- auto store = qStoreFromRGBA64PM[rasterBuffer->format];
- uchar *dest = rasterBuffer->scanLine(y);
- store(dest, buffer, x, length, nullptr, nullptr);
-}
-
-static void QT_FASTCALL destStore64RGBA64(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length)
-{
- QRgba64 *dest = reinterpret_cast<QRgba64*>(rasterBuffer->scanLine(y)) + x;
- for (int i = 0; i < length; ++i) {
- dest[i] = buffer[i].unpremultiplied();
- }
-}
-
static DestStoreProc destStoreProc[QImage::NImageFormats] =
{
0, // Format_Invalid
@@ -1935,6 +1924,22 @@ static DestStoreProc destStoreProc[QImage::NImageFormats] =
destStore, // Format_Grayscale16
};
+#if QT_CONFIG(raster_64bit)
+static void QT_FASTCALL destStore64(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length)
+{
+ auto store = qStoreFromRGBA64PM[rasterBuffer->format];
+ uchar *dest = rasterBuffer->scanLine(y);
+ store(dest, buffer, x, length, nullptr, nullptr);
+}
+
+static void QT_FASTCALL destStore64RGBA64(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length)
+{
+ QRgba64 *dest = reinterpret_cast<QRgba64*>(rasterBuffer->scanLine(y)) + x;
+ for (int i = 0; i < length; ++i) {
+ dest[i] = buffer[i].unpremultiplied();
+ }
+}
+
static DestStoreProc64 destStoreProc64[QImage::NImageFormats] =
{
0, // Format_Invalid
@@ -1967,6 +1972,7 @@ static DestStoreProc64 destStoreProc64[QImage::NImageFormats] =
0, // Format_RGBA64_Premultiplied
destStore64, // Format_Grayscale16
};
+#endif
/*
Source fetches
@@ -2017,6 +2023,7 @@ static const uint *QT_FASTCALL fetchUntransformedRGB16(uint *buffer, const Opera
return buffer;
}
+#if QT_CONFIG(raster_64bit)
static const QRgba64 *QT_FASTCALL fetchUntransformed64(QRgba64 *buffer, const Operator *,
const QSpanData *data, int y, int x, int length)
{
@@ -2030,6 +2037,7 @@ static const QRgba64 *QT_FASTCALL fetchUntransformedRGBA64PM(QRgba64 *, const Op
const uchar *scanLine = data->texture.scanLine(y);
return reinterpret_cast<const QRgba64 *>(scanLine) + x;
}
+#endif
template<TextureBlendType blendType>
inline void fetchTransformed_pixelBounds(int max, int l1, int l2, int &v)
@@ -2220,6 +2228,7 @@ static const uint *QT_FASTCALL fetchTransformed(uint *buffer, const Operator *,
return buffer;
}
+#if QT_CONFIG(raster_64bit)
template<TextureBlendType blendType> /* either BlendTransformed or BlendTransformedTiled */
static const QRgba64 *QT_FASTCALL fetchTransformed64(QRgba64 *buffer, const Operator *, const QSpanData *data,
int y, int x, int length)
@@ -2240,6 +2249,7 @@ static const QRgba64 *QT_FASTCALL fetchTransformed64(QRgba64 *buffer, const Oper
convertRGBA64ToRGBA64PM(buffer, length);
return buffer;
}
+#endif
/** \internal
interpolate 4 argb pixels with the distx and disty factor.
@@ -3541,6 +3551,7 @@ static const uint *QT_FASTCALL fetchTransformedBilinear(uint *buffer, const Oper
return buffer;
}
+#if QT_CONFIG(raster_64bit)
template<TextureBlendType blendType>
static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64_uint32(QRgba64 *buffer, const QSpanData *data,
int y, int x, int length)
@@ -3861,6 +3872,7 @@ static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64(QRgba64 *buffer, co
return fetchTransformedBilinear64_uint64<blendType>(buffer, data, y, x, length);
return fetchTransformedBilinear64_uint32<blendType>(buffer, data, y, x, length);
}
+#endif
// FetchUntransformed can have more specialized methods added depending on SIMD features.
static SourceFetchProc sourceFetchUntransformed[QImage::NImageFormats] = {
@@ -3931,6 +3943,20 @@ static SourceFetchProc sourceFetchAny32[NBlendTypes] = {
fetchTransformedBilinear<BlendTransformedBilinearTiled, QPixelLayout::BPP32> // TransformedBilinearTiled
};
+static inline SourceFetchProc getSourceFetch(TextureBlendType blendType, QImage::Format format)
+{
+ if (format == QImage::Format_RGB32 || format == QImage::Format_ARGB32_Premultiplied)
+ return sourceFetchARGB32PM[blendType];
+ if (blendType == BlendUntransformed || blendType == BlendTiled)
+ return sourceFetchUntransformed[format];
+ if (qPixelLayouts[format].bpp == QPixelLayout::BPP16)
+ return sourceFetchAny16[blendType];
+ if (qPixelLayouts[format].bpp == QPixelLayout::BPP32)
+ return sourceFetchAny32[blendType];
+ return sourceFetchGeneric[blendType];
+}
+
+#if QT_CONFIG(raster_64bit)
static const SourceFetchProc64 sourceFetchGeneric64[NBlendTypes] = {
fetchUntransformed64, // Untransformed
fetchUntransformed64, // Tiled
@@ -3949,25 +3975,13 @@ static const SourceFetchProc64 sourceFetchRGBA64PM[NBlendTypes] = {
fetchTransformedBilinear64<BlendTransformedBilinearTiled> // BilinearTiled
};
-static inline SourceFetchProc getSourceFetch(TextureBlendType blendType, QImage::Format format)
-{
- if (format == QImage::Format_RGB32 || format == QImage::Format_ARGB32_Premultiplied)
- return sourceFetchARGB32PM[blendType];
- if (blendType == BlendUntransformed || blendType == BlendTiled)
- return sourceFetchUntransformed[format];
- if (qPixelLayouts[format].bpp == QPixelLayout::BPP16)
- return sourceFetchAny16[blendType];
- if (qPixelLayouts[format].bpp == QPixelLayout::BPP32)
- return sourceFetchAny32[blendType];
- return sourceFetchGeneric[blendType];
-}
-
static inline SourceFetchProc64 getSourceFetch64(TextureBlendType blendType, QImage::Format format)
{
if (format == QImage::Format_RGBX64 || format == QImage::Format_RGBA64_Premultiplied)
return sourceFetchRGBA64PM[blendType];
return sourceFetchGeneric64[blendType];
}
+#endif
#define FIXPT_BITS 8
@@ -3979,11 +3993,13 @@ static uint qt_gradient_pixel_fixed(const QGradientData *data, int fixed_pos)
return data->colorTable32[qt_gradient_clamp(data, ipos)];
}
+#if QT_CONFIG(raster_64bit)
static const QRgba64& qt_gradient_pixel64_fixed(const QGradientData *data, int fixed_pos)
{
int ipos = (fixed_pos + (FIXPT_SIZE / 2)) >> FIXPT_BITS;
return data->colorTable64[qt_gradient_clamp(data, ipos)];
}
+#endif
static void QT_FASTCALL getLinearGradientValues(LinearGradientValues *v, const QSpanData *data)
{
@@ -4017,6 +4033,7 @@ public:
}
};
+#if QT_CONFIG(raster_64bit)
class GradientBase64
{
public:
@@ -4035,6 +4052,7 @@ public:
qt_memfill64((quint64*)buffer, fill, length);
}
};
+#endif
template<class GradientBase, typename BlendType>
static inline const BlendType * QT_FASTCALL qt_fetch_linear_gradient_template(
@@ -4112,11 +4130,13 @@ static const uint * QT_FASTCALL qt_fetch_linear_gradient(uint *buffer, const Ope
return qt_fetch_linear_gradient_template<GradientBase32, uint>(buffer, op, data, y, x, length);
}
+#if QT_CONFIG(raster_64bit)
static const QRgba64 * QT_FASTCALL qt_fetch_linear_gradient_rgb64(QRgba64 *buffer, const Operator *op, const QSpanData *data,
int y, int x, int length)
{
return qt_fetch_linear_gradient_template<GradientBase64, QRgba64>(buffer, op, data, y, x, length);
}
+#endif
static void QT_FASTCALL getRadialGradientValues(RadialGradientValues *v, const QSpanData *data)
{
@@ -4178,11 +4198,13 @@ const uint * QT_FASTCALL qt_fetch_radial_gradient_plain(uint *buffer, const Oper
static SourceFetchProc qt_fetch_radial_gradient = qt_fetch_radial_gradient_plain;
+#if QT_CONFIG(raster_64bit)
const QRgba64 * QT_FASTCALL qt_fetch_radial_gradient_rgb64(QRgba64 *buffer, const Operator *op, const QSpanData *data,
int y, int x, int length)
{
return qt_fetch_radial_gradient_template<RadialFetchPlain<GradientBase64>, QRgba64>(buffer, op, data, y, x, length);
}
+#endif
template <class GradientBase, typename BlendType>
static inline const BlendType * QT_FASTCALL qt_fetch_conical_gradient_template(
@@ -4241,23 +4263,29 @@ static const uint * QT_FASTCALL qt_fetch_conical_gradient(uint *buffer, const Op
return qt_fetch_conical_gradient_template<GradientBase32, uint>(buffer, data, y, x, length);
}
+#if QT_CONFIG(raster_64bit)
static const QRgba64 * QT_FASTCALL qt_fetch_conical_gradient_rgb64(QRgba64 *buffer, const Operator *, const QSpanData *data,
int y, int x, int length)
{
return qt_fetch_conical_gradient_template<GradientBase64, QRgba64>(buffer, data, y, x, length);
}
+#endif
extern CompositionFunctionSolid qt_functionForModeSolid_C[];
extern CompositionFunctionSolid64 qt_functionForModeSolid64_C[];
static const CompositionFunctionSolid *functionForModeSolid = qt_functionForModeSolid_C;
+#if QT_CONFIG(raster_64bit)
static const CompositionFunctionSolid64 *functionForModeSolid64 = qt_functionForModeSolid64_C;
+#endif
extern CompositionFunction qt_functionForMode_C[];
extern CompositionFunction64 qt_functionForMode64_C[];
static const CompositionFunction *functionForMode = qt_functionForMode_C;
+#if QT_CONFIG(raster_64bit)
static const CompositionFunction64 *functionForMode64 = qt_functionForMode64_C;
+#endif
static TextureBlendType getBlendType(const QSpanData *data)
{
@@ -4289,41 +4317,58 @@ static inline Operator getOperator(const QSpanData *data, const QSpan *spans, in
case QSpanData::Solid:
solidSource = data->solidColor.isOpaque();
op.srcFetch = 0;
+#if QT_CONFIG(raster_64bit)
op.srcFetch64 = 0;
+#endif
break;
case QSpanData::LinearGradient:
solidSource = !data->gradient.alphaColor;
getLinearGradientValues(&op.linear, data);
op.srcFetch = qt_fetch_linear_gradient;
+#if QT_CONFIG(raster_64bit)
op.srcFetch64 = qt_fetch_linear_gradient_rgb64;
+#endif
break;
case QSpanData::RadialGradient:
solidSource = !data->gradient.alphaColor;
getRadialGradientValues(&op.radial, data);
op.srcFetch = qt_fetch_radial_gradient;
+#if QT_CONFIG(raster_64bit)
op.srcFetch64 = qt_fetch_radial_gradient_rgb64;
+#endif
break;
case QSpanData::ConicalGradient:
solidSource = !data->gradient.alphaColor;
op.srcFetch = qt_fetch_conical_gradient;
+#if QT_CONFIG(raster_64bit)
op.srcFetch64 = qt_fetch_conical_gradient_rgb64;
+#endif
break;
case QSpanData::Texture:
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);;
+#endif
break;
default:
Q_UNREACHABLE();
break;
}
+#if !QT_CONFIG(raster_64bit)
+ op.srcFetch64 = 0;
+#endif
op.mode = data->rasterBuffer->compositionMode;
if (op.mode == QPainter::CompositionMode_SourceOver && solidSource)
op.mode = QPainter::CompositionMode_Source;
op.destFetch = destFetchProc[data->rasterBuffer->format];
+#if QT_CONFIG(raster_64bit)
op.destFetch64 = destFetchProc64[data->rasterBuffer->format];
+#else
+ op.destFetch64 = 0;
+#endif
if (op.mode == QPainter::CompositionMode_Source &&
(data->type != QSpanData::Texture || data->texture.const_alpha == 256)) {
const QSpan *lastSpan = spans + spanCount;
@@ -4340,18 +4385,25 @@ static inline Operator getOperator(const QSpanData *data, const QSpan *spans, in
// But don't clear passthrough destFetch as they are just as fast and save destStore.
if (op.destFetch != destFetchARGB32P)
op.destFetch = destFetchUndefined;
+#if QT_CONFIG(raster_64bit)
if (op.destFetch64 != destFetchRGB64)
op.destFetch64 = destFetch64Undefined;
+#endif
}
}
op.destStore = destStoreProc[data->rasterBuffer->format];
- op.destStore64 = destStoreProc64[data->rasterBuffer->format];
-
op.funcSolid = functionForModeSolid[op.mode];
- op.funcSolid64 = functionForModeSolid64[op.mode];
op.func = functionForMode[op.mode];
+#if QT_CONFIG(raster_64bit)
+ op.destStore64 = destStoreProc64[data->rasterBuffer->format];
+ op.funcSolid64 = functionForModeSolid64[op.mode];
op.func64 = functionForMode64[op.mode];
+#else
+ op.destStore64 = 0;
+ op.funcSolid64 = 0;
+ op.func64 = 0;
+#endif
return op;
}
@@ -4458,6 +4510,7 @@ static void blend_color_argb(int count, const QSpan *spans, void *userData)
void blend_color_generic_rgb64(int count, const QSpan *spans, void *userData)
{
+#if QT_CONFIG(raster_64bit)
QSpanData *data = reinterpret_cast<QSpanData *>(userData);
Operator op = getOperator(data, nullptr, 0);
if (!op.funcSolid64) {
@@ -4492,6 +4545,9 @@ void blend_color_generic_rgb64(int count, const QSpan *spans, void *userData)
}
++spans;
}
+#else
+ blend_color_generic(count, spans, userData);
+#endif
}
static void blend_color_rgb16(int count, const QSpan *spans, void *userData)
@@ -4665,6 +4721,7 @@ public:
}
};
+#if QT_CONFIG(raster_64bit)
class BlendSrcGenericRGB64 : public QBlendBase<QRgba64>
{
public:
@@ -4695,6 +4752,7 @@ public:
op.destStore64(data->rasterBuffer, x, y, dest, len);
}
};
+#endif
static void blend_src_generic(int count, const QSpan *spans, void *userData)
{
@@ -4703,6 +4761,7 @@ static void blend_src_generic(int count, const QSpan *spans, void *userData)
handleSpans(count, spans, data, blend);
}
+#if QT_CONFIG(raster_64bit)
static void blend_src_generic_rgb64(int count, const QSpan *spans, void *userData)
{
QSpanData *data = reinterpret_cast<QSpanData *>(userData);
@@ -4716,6 +4775,7 @@ static void blend_src_generic_rgb64(int count, const QSpan *spans, void *userDat
handleSpans(count, spans, data, blend32);
}
}
+#endif
static void blend_untransformed_generic(int count, const QSpan *spans, void *userData)
{
@@ -4762,6 +4822,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)
{
QSpanData *data = reinterpret_cast<QSpanData *>(userData);
@@ -4810,6 +4871,7 @@ static void blend_untransformed_generic_rgb64(int count, const QSpan *spans, voi
++spans;
}
}
+#endif
static void blend_untransformed_argb(int count, const QSpan *spans, void *userData)
{
@@ -5004,6 +5066,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)
{
QSpanData *data = reinterpret_cast<QSpanData *>(userData);
@@ -5099,6 +5162,7 @@ static void blend_tiled_generic_rgb64(int count, const QSpan *spans, void *userD
++spans;
}
}
+#endif
static void blend_tiled_argb(int count, const QSpan *spans, void *userData)
{
@@ -5275,6 +5339,7 @@ static const ProcessSpans processTextureSpansGeneric[NBlendTypes] = {
blend_src_generic // TransformedBilinearTiled
};
+#if QT_CONFIG(raster_64bit)
static const ProcessSpans processTextureSpansGeneric64[NBlendTypes] = {
blend_untransformed_generic_rgb64, // Untransformed
blend_tiled_generic_rgb64, // Tiled
@@ -5283,6 +5348,7 @@ static const ProcessSpans processTextureSpansGeneric64[NBlendTypes] = {
blend_src_generic_rgb64, // TransformedBilinear
blend_src_generic_rgb64 // TransformedBilinearTiled
};
+#endif
void qBlendTexture(int count, const QSpan *spans, void *userData)
{
@@ -5296,6 +5362,7 @@ void qBlendTexture(int count, const QSpan *spans, void *userData)
case QImage::Format_RGB16:
proc = processTextureSpansRGB16[blendType];
break;
+#if QT_CONFIG(raster_64bit)
#if defined(__SSE2__) || defined(__ARM_NEON__) || (Q_PROCESSOR_WORDSIZE == 8)
case QImage::Format_ARGB32:
case QImage::Format_RGBA8888:
@@ -5310,6 +5377,7 @@ void qBlendTexture(int count, const QSpan *spans, void *userData)
case QImage::Format_Grayscale16:
proc = processTextureSpansGeneric64[blendType];
break;
+#endif // QT_CONFIG(raster_64bit)
case QImage::Format_Invalid:
Q_UNREACHABLE();
return;
@@ -5377,7 +5445,11 @@ static void blend_vertical_gradient(int count, const QSpan *spans, void *userDat
while (count--) {
int y = spans->y;
+#if QT_CONFIG(raster_64bit)
data->solidColor = qt_gradient_pixel64_fixed(&data->gradient, yinc * y + off);
+#else
+ data->solidColor = QRgba64::fromArgb32(qt_gradient_pixel_fixed(&data->gradient, yinc * y + off));
+#endif
blend_color(1, spans, userData);
++spans;
}
@@ -5400,6 +5472,7 @@ void qBlendGradient(int count, const QSpan *spans, void *userData)
if (isVerticalGradient)
return blend_vertical_gradient_argb(count, spans, userData);
return blend_src_generic(count, spans, userData);
+#if QT_CONFIG(raster_64bit)
#if defined(__SSE2__) || defined(__ARM_NEON__) || (Q_PROCESSOR_WORDSIZE == 8)
case QImage::Format_ARGB32:
case QImage::Format_RGBA8888:
@@ -5414,6 +5487,7 @@ void qBlendGradient(int count, const QSpan *spans, void *userData)
if (isVerticalGradient)
return blend_vertical_gradient<blend_color_generic_rgb64>(count, spans, userData);
return blend_src_generic_rgb64(count, spans, userData);
+#endif // QT_CONFIG(raster_64bit)
case QImage::Format_Invalid:
break;
default:
@@ -5523,7 +5597,36 @@ inline static void qt_bitmapblit_quint16(QRasterBuffer *rasterBuffer,
map, mapWidth, mapHeight, mapStride);
}
-static inline void alphamapblend_generic(int coverage, QRgba64 *dest, int x, const QRgba64 &srcLinear, const QRgba64 &src, const QColorProfile *colorProfile)
+static inline void grayBlendPixel(quint32 *dst, int coverage, QRgba64 srcLinear, const QColorTrcLut *colorProfile)
+{
+ // Do a gammacorrected gray alphablend...
+ const QRgba64 dstLinear = colorProfile ? colorProfile->toLinear64(*dst) : QRgba64::fromArgb32(*dst);
+
+ QRgba64 blend = interpolate255(srcLinear, coverage, dstLinear, 255 - coverage);
+
+ *dst = colorProfile ? colorProfile->fromLinear64(blend) : toArgb32(blend);
+}
+
+static inline void alphamapblend_argb32(quint32 *dst, int coverage, QRgba64 srcLinear, quint32 src, const QColorTrcLut *colorProfile)
+{
+ if (coverage == 0) {
+ // nothing
+ } else if (coverage == 255) {
+ *dst = src;
+ } else if (!colorProfile) {
+ *dst = INTERPOLATE_PIXEL_255(src, coverage, *dst, 255 - coverage);
+ } else {
+ if (*dst >= 0xff000000) {
+ grayBlendPixel(dst, coverage, srcLinear, colorProfile);
+ } else {
+ // Give up and do a naive gray alphablend. Needed to deal with ARGB32 and invalid ARGB32_premultiplied, see QTBUG-60571
+ *dst = INTERPOLATE_PIXEL_255(src, coverage, *dst, 255 - coverage);
+ }
+ }
+}
+
+#if QT_CONFIG(raster_64bit)
+static inline void alphamapblend_generic(int coverage, QRgba64 *dest, int x, const QRgba64 &srcLinear, const QRgba64 &src, const QColorTrcLut *colorProfile)
{
if (coverage == 0) {
// nothing
@@ -5558,7 +5661,7 @@ static void qt_alphamapblit_generic(QRasterBuffer *rasterBuffer,
if (color.isTransparent())
return;
- const QColorProfile *colorProfile = nullptr;
+ const QColorTrcLut *colorProfile = nullptr;
if (useGammaCorrection)
colorProfile = QGuiApplicationPrivate::instance()->colorProfileForA8Text();
@@ -5624,6 +5727,85 @@ static void qt_alphamapblit_generic(QRasterBuffer *rasterBuffer,
} // for (yp -> bottom)
}
}
+#else
+static void qt_alphamapblit_generic(QRasterBuffer *rasterBuffer,
+ int x, int y, const QRgba64 &color,
+ const uchar *map,
+ int mapWidth, int mapHeight, int mapStride,
+ const QClipData *clip, bool useGammaCorrection)
+{
+ if (color.isTransparent())
+ return;
+
+ const quint32 c = color.toArgb32();
+
+ const QColorTrcLut *colorProfile = nullptr;
+
+ if (useGammaCorrection)
+ colorProfile = QGuiApplicationPrivate::instance()->colorProfileForA8Text();
+
+ QRgba64 srcColor = color;
+ if (colorProfile) {
+ if (color.isOpaque())
+ srcColor = colorProfile->toLinear(srcColor);
+ else
+ srcColor = colorProfile->toLinear(srcColor.unpremultiplied()).premultiplied();
+ }
+
+ quint32 buffer[BufferSize];
+ const DestFetchProc destFetch = destFetchProc[rasterBuffer->format];
+ const DestStoreProc destStore = destStoreProc[rasterBuffer->format];
+
+ if (!clip) {
+ for (int ly = 0; ly < mapHeight; ++ly) {
+ int i = x;
+ int length = mapWidth;
+ while (length > 0) {
+ int l = qMin(BufferSize, length);
+ quint32 *dest = destFetch(buffer, rasterBuffer, i, y + ly, l);
+ for (int j=0; j < l; ++j) {
+ const int coverage = map[j + (i - x)];
+ alphamapblend_argb32(dest + j, coverage, srcColor, c, colorProfile);
+ }
+ if (destStore)
+ destStore(rasterBuffer, i, y + ly, dest, l);
+ length -= l;
+ i += l;
+ }
+ map += mapStride;
+ }
+ } else {
+ int bottom = qMin(y + mapHeight, rasterBuffer->height());
+
+ int top = qMax(y, 0);
+ map += (top - y) * mapStride;
+
+ const_cast<QClipData *>(clip)->initialize();
+ for (int yp = top; yp<bottom; ++yp) {
+ const QClipData::ClipLine &line = clip->m_clipLines[yp];
+
+ for (int i=0; i<line.count; ++i) {
+ const QSpan &clip = line.spans[i];
+
+ int start = qMax<int>(x, clip.x);
+ int end = qMin<int>(x + mapWidth, clip.x + clip.len);
+ if (end <= start)
+ continue;
+ Q_ASSERT(end - start <= BufferSize);
+ quint32 *dest = destFetch(buffer, rasterBuffer, start, clip.y, end - start);
+
+ for (int xp=start; xp<end; ++xp) {
+ const int coverage = map[xp - x];
+ alphamapblend_argb32(dest + xp - x, coverage, srcColor, color, colorProfile);
+ }
+ if (destStore)
+ destStore(rasterBuffer, start, clip.y, dest, end - start);
+ } // for (i -> line.count)
+ map += mapStride;
+ } // for (yp -> bottom)
+ }
+}
+#endif
static inline void alphamapblend_quint16(int coverage, quint16 *dest, int x, const quint16 srcColor)
{
@@ -5684,44 +5866,6 @@ void qt_alphamapblit_quint16(QRasterBuffer *rasterBuffer,
}
}
-static inline void rgbBlendPixel(quint32 *dst, int coverage, QRgba64 slinear, const QColorProfile *colorProfile)
-{
- // Do a gammacorrected RGB alphablend...
- const QRgba64 dlinear = colorProfile ? colorProfile->toLinear64(*dst) : QRgba64::fromArgb32(*dst);
-
- QRgba64 blend = rgbBlend(dlinear, slinear, coverage);
-
- *dst = colorProfile ? colorProfile->fromLinear64(blend) : toArgb32(blend);
-}
-
-static inline void grayBlendPixel(quint32 *dst, int coverage, QRgba64 srcLinear, const QColorProfile *colorProfile)
-{
- // Do a gammacorrected gray alphablend...
- const QRgba64 dstLinear = colorProfile ? colorProfile->toLinear64(*dst) : QRgba64::fromArgb32(*dst);
-
- QRgba64 blend = interpolate255(srcLinear, coverage, dstLinear, 255 - coverage);
-
- *dst = colorProfile ? colorProfile->fromLinear64(blend) : toArgb32(blend);
-}
-
-static inline void alphamapblend_argb32(quint32 *dst, int coverage, QRgba64 srcLinear, quint32 src, const QColorProfile *colorProfile)
-{
- if (coverage == 0) {
- // nothing
- } else if (coverage == 255) {
- *dst = src;
- } else if (!colorProfile) {
- *dst = INTERPOLATE_PIXEL_255(src, coverage, *dst, 255 - coverage);
- } else {
- if (*dst >= 0xff000000) {
- grayBlendPixel(dst, coverage, srcLinear, colorProfile);
- } else {
- // Give up and do a naive gray alphablend. Needed to deal with ARGB32 and invalid ARGB32_premultiplied, see QTBUG-60571
- *dst = INTERPOLATE_PIXEL_255(src, coverage, *dst, 255 - coverage);
- }
- }
-}
-
static void qt_alphamapblit_argb32(QRasterBuffer *rasterBuffer,
int x, int y, const QRgba64 &color,
const uchar *map,
@@ -5734,7 +5878,7 @@ static void qt_alphamapblit_argb32(QRasterBuffer *rasterBuffer,
if (color.isTransparent())
return;
- const QColorProfile *colorProfile = nullptr;
+ const QColorTrcLut *colorProfile = nullptr;
if (useGammaCorrection)
colorProfile = QGuiApplicationPrivate::instance()->colorProfileForA8Text();
@@ -5790,6 +5934,16 @@ static inline int qRgbAvg(QRgb rgb)
return (qRed(rgb) * 5 + qGreen(rgb) * 6 + qBlue(rgb) * 5) / 16;
}
+static inline void rgbBlendPixel(quint32 *dst, int coverage, QRgba64 slinear, const QColorTrcLut *colorProfile)
+{
+ // Do a gammacorrected RGB alphablend...
+ const QRgba64 dlinear = colorProfile ? colorProfile->toLinear64(*dst) : QRgba64::fromArgb32(*dst);
+
+ QRgba64 blend = rgbBlend(dlinear, slinear, coverage);
+
+ *dst = colorProfile ? colorProfile->fromLinear64(blend) : toArgb32(blend);
+}
+
static inline QRgb rgbBlend(QRgb d, QRgb s, uint rgbAlpha)
{
#if defined(__SSE2__)
@@ -5830,7 +5984,8 @@ static inline QRgb rgbBlend(QRgb d, QRgb s, uint rgbAlpha)
#endif
}
-static inline void alphargbblend_generic(uint coverage, QRgba64 *dest, int x, const QRgba64 &srcLinear, const QRgba64 &src, const QColorProfile *colorProfile)
+#if QT_CONFIG(raster_64bit)
+static inline void alphargbblend_generic(uint coverage, QRgba64 *dest, int x, const QRgba64 &srcLinear, const QRgba64 &src, const QColorTrcLut *colorProfile)
{
if (coverage == 0xff000000) {
// nothing
@@ -5851,8 +6006,9 @@ static inline void alphargbblend_generic(uint coverage, QRgba64 *dest, int x, co
}
}
}
+#endif
-static inline void alphargbblend_argb32(quint32 *dst, uint coverage, const QRgba64 &srcLinear, quint32 src, const QColorProfile *colorProfile)
+static inline void alphargbblend_argb32(quint32 *dst, uint coverage, const QRgba64 &srcLinear, quint32 src, const QColorTrcLut *colorProfile)
{
if (coverage == 0xff000000) {
// nothing
@@ -5869,6 +6025,7 @@ static inline void alphargbblend_argb32(quint32 *dst, uint coverage, const QRgba
}
}
+#if QT_CONFIG(raster_64bit)
static void qt_alphargbblit_generic(QRasterBuffer *rasterBuffer,
int x, int y, const QRgba64 &color,
const uint *src, int mapWidth, int mapHeight, int srcStride,
@@ -5877,7 +6034,7 @@ static void qt_alphargbblit_generic(QRasterBuffer *rasterBuffer,
if (color.isTransparent())
return;
- const QColorProfile *colorProfile = nullptr;
+ const QColorTrcLut *colorProfile = nullptr;
if (useGammaCorrection)
colorProfile = QGuiApplicationPrivate::instance()->colorProfileForA32Text();
@@ -5943,6 +6100,84 @@ static void qt_alphargbblit_generic(QRasterBuffer *rasterBuffer,
} // for (yp -> bottom)
}
}
+#else
+static void qt_alphargbblit_generic(QRasterBuffer *rasterBuffer,
+ int x, int y, const QRgba64 &color,
+ const uint *src, int mapWidth, int mapHeight, int srcStride,
+ const QClipData *clip, bool useGammaCorrection)
+{
+ if (color.isTransparent())
+ return;
+
+ const quint32 c = color.toArgb32();
+
+ const QColorTrcLut *colorProfile = nullptr;
+
+ if (useGammaCorrection)
+ colorProfile = QGuiApplicationPrivate::instance()->colorProfileForA32Text();
+
+ QRgba64 srcColor = color;
+ if (colorProfile) {
+ if (color.isOpaque())
+ srcColor = colorProfile->toLinear(srcColor);
+ else
+ srcColor = colorProfile->toLinear(srcColor.unpremultiplied()).premultiplied();
+ }
+
+ quint32 buffer[BufferSize];
+ const DestFetchProc destFetch = destFetchProc[rasterBuffer->format];
+ const DestStoreProc destStore = destStoreProc[rasterBuffer->format];
+
+ if (!clip) {
+ for (int ly = 0; ly < mapHeight; ++ly) {
+ int i = x;
+ int length = mapWidth;
+ while (length > 0) {
+ int l = qMin(BufferSize, length);
+ quint32 *dest = destFetch(buffer, rasterBuffer, i, y + ly, l);
+ for (int j=0; j < l; ++j) {
+ const uint coverage = src[j + (i - x)];
+ alphargbblend_argb32(dest + j, coverage, srcColor, c, colorProfile);
+ }
+ if (destStore)
+ destStore(rasterBuffer, i, y + ly, dest, l);
+ length -= l;
+ i += l;
+ }
+ src += srcStride;
+ }
+ } else {
+ int bottom = qMin(y + mapHeight, rasterBuffer->height());
+
+ int top = qMax(y, 0);
+ src += (top - y) * srcStride;
+
+ const_cast<QClipData *>(clip)->initialize();
+ for (int yp = top; yp<bottom; ++yp) {
+ const QClipData::ClipLine &line = clip->m_clipLines[yp];
+
+ for (int i=0; i<line.count; ++i) {
+ const QSpan &clip = line.spans[i];
+
+ int start = qMax<int>(x, clip.x);
+ int end = qMin<int>(x + mapWidth, clip.x + clip.len);
+ if (end <= start)
+ continue;
+ Q_ASSERT(end - start <= BufferSize);
+ quint32 *dest = destFetch(buffer, rasterBuffer, start, clip.y, end - start);
+
+ for (int xp=start; xp<end; ++xp) {
+ const uint coverage = src[xp - x];
+ alphargbblend_argb32(dest + xp - start, coverage, srcColor, c, colorProfile);
+ }
+ if (destStore)
+ destStore(rasterBuffer, start, clip.y, dest, end - start);
+ } // for (i -> line.count)
+ src += srcStride;
+ } // for (yp -> bottom)
+ }
+}
+#endif
static void qt_alphargbblit_argb32(QRasterBuffer *rasterBuffer,
int x, int y, const QRgba64 &color,
@@ -5954,7 +6189,7 @@ static void qt_alphargbblit_argb32(QRasterBuffer *rasterBuffer,
const quint32 c = color.toArgb32();
- const QColorProfile *colorProfile = nullptr;
+ const QColorTrcLut *colorProfile = nullptr;
if (useGammaCorrection)
colorProfile = QGuiApplicationPrivate::instance()->colorProfileForA32Text();
@@ -6533,8 +6768,10 @@ static void qInitDrawhelperFunctions()
qPixelLayouts[QImage::Format_A2RGB30_Premultiplied].storeFromARGB32PM = storeA2RGB30PMFromARGB32PM_sse4<PixelOrderRGB>;
qStoreFromRGBA64PM[QImage::Format_ARGB32] = storeARGB32FromRGBA64PM_sse4;
qStoreFromRGBA64PM[QImage::Format_RGBA8888] = storeRGBA8888FromRGBA64PM_sse4;
+#if QT_CONFIG(raster_64bit)
destStoreProc64[QImage::Format_ARGB32] = destStore64ARGB32_sse4;
destStoreProc64[QImage::Format_RGBA8888] = destStore64RGBA8888_sse4;
+#endif
}
#endif
@@ -6558,18 +6795,19 @@ static void qInitDrawhelperFunctions()
qBlendFunctions[QImage::Format_RGBA8888_Premultiplied][QImage::Format_RGBA8888_Premultiplied] = qt_blend_argb32_on_argb32_avx2;
extern void QT_FASTCALL comp_func_Source_avx2(uint *destPixels, const uint *srcPixels, int length, uint const_alpha);
- extern void QT_FASTCALL comp_func_Source_rgb64_avx2(QRgba64 *destPixels, const QRgba64 *srcPixels, int length, uint const_alpha);
extern void QT_FASTCALL comp_func_SourceOver_avx2(uint *destPixels, const uint *srcPixels, int length, uint const_alpha);
- extern void QT_FASTCALL comp_func_SourceOver_rgb64_avx2(QRgba64 *destPixels, const QRgba64 *srcPixels, int length, uint const_alpha);
extern void QT_FASTCALL comp_func_solid_SourceOver_avx2(uint *destPixels, int length, uint color, uint const_alpha);
- extern void QT_FASTCALL comp_func_solid_SourceOver_rgb64_avx2(QRgba64 *destPixels, int length, QRgba64 color, uint const_alpha);
-
qt_functionForMode_C[QPainter::CompositionMode_Source] = comp_func_Source_avx2;
- qt_functionForMode64_C[QPainter::CompositionMode_Source] = comp_func_Source_rgb64_avx2;
qt_functionForMode_C[QPainter::CompositionMode_SourceOver] = comp_func_SourceOver_avx2;
- qt_functionForMode64_C[QPainter::CompositionMode_SourceOver] = comp_func_SourceOver_rgb64_avx2;
qt_functionForModeSolid_C[QPainter::CompositionMode_SourceOver] = comp_func_solid_SourceOver_avx2;
+#if QT_CONFIG(raster_64bit)
+ extern void QT_FASTCALL comp_func_Source_rgb64_avx2(QRgba64 *destPixels, const QRgba64 *srcPixels, int length, uint const_alpha);
+ extern void QT_FASTCALL comp_func_SourceOver_rgb64_avx2(QRgba64 *destPixels, const QRgba64 *srcPixels, int length, uint const_alpha);
+ extern void QT_FASTCALL comp_func_solid_SourceOver_rgb64_avx2(QRgba64 *destPixels, int length, QRgba64 color, uint const_alpha);
+ qt_functionForMode64_C[QPainter::CompositionMode_Source] = comp_func_Source_rgb64_avx2;
+ qt_functionForMode64_C[QPainter::CompositionMode_SourceOver] = comp_func_SourceOver_rgb64_avx2;
qt_functionForModeSolid64_C[QPainter::CompositionMode_SourceOver] = comp_func_solid_SourceOver_rgb64_avx2;
+#endif
extern void QT_FASTCALL fetchTransformedBilinearARGB32PM_simple_scale_helper_avx2(uint *b, uint *end, const QTextureData &image,
int &fx, int &fy, int fdx, int /*fdy*/);
@@ -6593,6 +6831,7 @@ static void qInitDrawhelperFunctions()
qPixelLayouts[QImage::Format_RGBA8888].fetchToARGB32PM = fetchRGBA8888ToARGB32PM_avx2;
qPixelLayouts[QImage::Format_RGBA8888].convertToARGB32PM = convertRGBA8888ToARGB32PM_avx2;
+#if QT_CONFIG(raster_64bit)
extern const QRgba64 * QT_FASTCALL convertARGB32ToRGBA64PM_avx2(QRgba64 *, const uint *, int, const QVector<QRgb> *, QDitherInfo *);
extern const QRgba64 * QT_FASTCALL convertRGBA8888ToRGBA64PM_avx2(QRgba64 *, const uint *, int count, const QVector<QRgb> *, QDitherInfo *);
extern const QRgba64 *QT_FASTCALL fetchARGB32ToRGBA64PM_avx2(QRgba64 *, const uchar *, int, int, const QVector<QRgb> *, QDitherInfo *);
@@ -6601,6 +6840,7 @@ static void qInitDrawhelperFunctions()
qPixelLayouts[QImage::Format_RGBX8888].convertToRGBA64PM = convertRGBA8888ToRGBA64PM_avx2;
qPixelLayouts[QImage::Format_ARGB32].fetchToRGBA64PM = fetchARGB32ToRGBA64PM_avx2;
qPixelLayouts[QImage::Format_RGBX8888].fetchToRGBA64PM = fetchRGBA8888ToRGBA64PM_avx2;
+#endif
}
#endif
diff --git a/src/gui/painting/qdrawhelper_avx2.cpp b/src/gui/painting/qdrawhelper_avx2.cpp
index 2b3cc9b226..fb50cb6a50 100644
--- a/src/gui/painting/qdrawhelper_avx2.cpp
+++ b/src/gui/painting/qdrawhelper_avx2.cpp
@@ -396,6 +396,7 @@ void QT_FASTCALL comp_func_SourceOver_avx2(uint *destPixels, const uint *srcPixe
BLEND_SOURCE_OVER_ARGB32_WITH_CONST_ALPHA_AVX2(dst, src, length, const_alpha);
}
+#if QT_CONFIG(raster_64bit)
void QT_FASTCALL comp_func_SourceOver_rgb64_avx2(QRgba64 *dst, const QRgba64 *src, int length, uint const_alpha)
{
Q_ASSERT(const_alpha < 256); // const_alpha is in [0-255]
@@ -453,6 +454,7 @@ void QT_FASTCALL comp_func_SourceOver_rgb64_avx2(QRgba64 *dst, const QRgba64 *sr
blend_pixel(dst[x], src[x], const_alpha);
}
}
+#endif
void QT_FASTCALL comp_func_Source_avx2(uint *dst, const uint *src, int length, uint const_alpha)
{
@@ -485,6 +487,7 @@ void QT_FASTCALL comp_func_Source_avx2(uint *dst, const uint *src, int length, u
}
}
+#if QT_CONFIG(raster_64bit)
void QT_FASTCALL comp_func_Source_rgb64_avx2(QRgba64 *dst, const QRgba64 *src, int length, uint const_alpha)
{
Q_ASSERT(const_alpha < 256); // const_alpha is in [0-255]
@@ -517,6 +520,7 @@ void QT_FASTCALL comp_func_Source_rgb64_avx2(QRgba64 *dst, const QRgba64 *src, i
dst[x] = interpolate65535(src[x], ca, dst[x], cia);
}
}
+#endif
void QT_FASTCALL comp_func_solid_SourceOver_avx2(uint *destPixels, int length, uint color, uint const_alpha)
{
@@ -549,6 +553,7 @@ void QT_FASTCALL comp_func_solid_SourceOver_avx2(uint *destPixels, int length, u
}
}
+#if QT_CONFIG(raster_64bit)
void QT_FASTCALL comp_func_solid_SourceOver_rgb64_avx2(QRgba64 *destPixels, int length, QRgba64 color, uint const_alpha)
{
Q_ASSERT(const_alpha < 256); // const_alpha is in [0-255]
@@ -579,6 +584,7 @@ void QT_FASTCALL comp_func_solid_SourceOver_rgb64_avx2(QRgba64 *destPixels, int
destPixels[x] = color + multiplyAlpha65535(destPixels[x], minusAlphaOfColor);
}
}
+#endif
#define interpolate_4_pixels_16_avx2(tlr1, tlr2, blr1, blr2, distx, disty, colorMask, v_256, b) \
{ \
diff --git a/src/gui/painting/qdrawhelper_p.h b/src/gui/painting/qdrawhelper_p.h
index 37108949d6..9c5d525722 100644
--- a/src/gui/painting/qdrawhelper_p.h
+++ b/src/gui/painting/qdrawhelper_p.h
@@ -296,7 +296,9 @@ struct QGradientData
#define GRADIENT_STOPTABLE_SIZE 1024
#define GRADIENT_STOPTABLE_SIZE_SHIFT 10
+#if QT_CONFIG(raster_64bit)
const QRgba64 *colorTable64; //[GRADIENT_STOPTABLE_SIZE];
+#endif
const QRgb *colorTable32; //[GRADIENT_STOPTABLE_SIZE];
uint alphaColor : 1;
@@ -328,7 +330,7 @@ struct QTextureData
struct QSpanData
{
- QSpanData() : tempImage(0) {}
+ QSpanData() : tempImage(nullptr) {}
~QSpanData() { delete tempImage; }
QRasterBuffer *rasterBuffer;
@@ -402,11 +404,13 @@ static inline uint qt_gradient_pixel(const QGradientData *data, qreal pos)
return data->colorTable32[qt_gradient_clamp(data, ipos)];
}
+#if QT_CONFIG(raster_64bit)
static inline const QRgba64& qt_gradient_pixel64(const QGradientData *data, qreal pos)
{
int ipos = int(pos * (GRADIENT_STOPTABLE_SIZE - 1) + qreal(0.5));
return data->colorTable64[qt_gradient_clamp(data, ipos)];
}
+#endif
static inline qreal qRadialDeterminant(qreal a, qreal b, qreal c)
{
diff --git a/src/gui/painting/qdrawhelper_sse4.cpp b/src/gui/painting/qdrawhelper_sse4.cpp
index e387026972..68d887ae6d 100644
--- a/src/gui/painting/qdrawhelper_sse4.cpp
+++ b/src/gui/painting/qdrawhelper_sse4.cpp
@@ -417,6 +417,7 @@ void QT_FASTCALL storeA2RGB30PMFromARGB32PM_sse4(uchar *dest, const uint *src, i
d[i] = qConvertArgb32ToA2rgb30_sse4<PixelOrder>(src[i]);
}
+#if QT_CONFIG(raster_64bit)
void QT_FASTCALL destStore64ARGB32_sse4(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length)
{
uint *dest = (uint*)rasterBuffer->scanLine(y) + x;
@@ -428,6 +429,7 @@ void QT_FASTCALL destStore64RGBA8888_sse4(QRasterBuffer *rasterBuffer, int x, in
uint *dest = (uint*)rasterBuffer->scanLine(y) + x;
convertARGBFromRGBA64PM_sse4<true>(dest, buffer, length);
}
+#endif
void QT_FASTCALL storeARGB32FromRGBA64PM_sse4(uchar *dest, const QRgba64 *src, int index, int count,
const QVector<QRgb> *, QDitherInfo *)
diff --git a/src/gui/painting/qicc.cpp b/src/gui/painting/qicc.cpp
new file mode 100644
index 0000000000..d88b005782
--- /dev/null
+++ b/src/gui/painting/qicc.cpp
@@ -0,0 +1,669 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qicc_p.h"
+
+#include <qbuffer.h>
+#include <qbytearray.h>
+#include <qdatastream.h>
+#include <qloggingcategory.h>
+#include <qendian.h>
+
+#include "qcolorspace_p.h"
+#include "qcolortrc_p.h"
+
+QT_BEGIN_NAMESPACE
+Q_LOGGING_CATEGORY(lcIcc, "qt.gui.icc")
+
+struct ICCProfileHeader
+{
+ quint32_be profileSize;
+
+ quint32_be preferredCmmType;
+
+ quint32_be profileVersion;
+ quint32_be profileClass;
+ quint32_be inputColorSpace;
+ quint32_be pcs;
+ quint32_be datetime[3];
+ quint32_be signature;
+ quint32_be platformSignature;
+ quint32_be flags;
+ quint32_be deviceManufacturer;
+ quint32_be deviceModel;
+ quint32_be deviceAttributes[2];
+
+ quint32_be renderingIntent;
+ qint32_be illuminantXyz[3];
+
+ quint32_be creatorSignature;
+ quint32_be profileId[4];
+
+ quint32_be reserved[7];
+
+// Technically after the header, but easier to include here:
+ quint32_be tagCount;
+};
+
+constexpr quint32 IccTag(uchar a, uchar b, uchar c, uchar d)
+{
+ return (a << 24) | (b << 16) | (c << 8) | d;
+}
+
+enum class ProfileClass : quint32 {
+ Input = IccTag('s', 'c', 'r', 'n'),
+ Display = IccTag('m', 'n', 't', 'r'),
+ // Not supported:
+ Output = IccTag('p', 'r', 't', 'r'),
+ ColorSpace = IccTag('s', 'p', 'a', 'c'),
+};
+
+enum class Tag : quint32 {
+ acsp = IccTag('a', 'c', 's', 'p'),
+ RGB_ = IccTag('R', 'G', 'B', ' '),
+ XYZ_ = IccTag('X', 'Y', 'Z', ' '),
+ rXYZ = IccTag('r', 'X', 'Y', 'Z'),
+ gXYZ = IccTag('g', 'X', 'Y', 'Z'),
+ bXYZ = IccTag('b', 'X', 'Y', 'Z'),
+ rTRC = IccTag('r', 'T', 'R', 'C'),
+ gTRC = IccTag('g', 'T', 'R', 'C'),
+ bTRC = IccTag('b', 'T', 'R', 'C'),
+ A2B0 = IccTag('A', '2', 'B', '0'),
+ A2B1 = IccTag('A', '2', 'B', '1'),
+ B2A0 = IccTag('B', '2', 'A', '0'),
+ B2A1 = IccTag('B', '2', 'A', '1'),
+ desc = IccTag('d', 'e', 's', 'c'),
+ text = IccTag('t', 'e', 'x', 't'),
+ cprt = IccTag('c', 'p', 'r', 't'),
+ curv = IccTag('c', 'u', 'r', 'v'),
+ para = IccTag('p', 'a', 'r', 'a'),
+ wtpt = IccTag('w', 't', 'p', 't'),
+ bkpt = IccTag('b', 'k', 'p', 't'),
+ mft1 = IccTag('m', 'f', 't', '1'),
+ mft2 = IccTag('m', 'f', 't', '2'),
+ mAB_ = IccTag('m', 'A', 'B', ' '),
+ mBA_ = IccTag('m', 'B', 'A', ' '),
+ chad = IccTag('c', 'h', 'a', 'd'),
+ sf32 = IccTag('s', 'f', '3', '2'),
+
+ // Apple extensions for ICCv2:
+ aarg = IccTag('a', 'a', 'r', 'g'),
+ aagg = IccTag('a', 'a', 'g', 'g'),
+ aabg = IccTag('a', 'a', 'b', 'g'),
+};
+
+inline uint qHash(const Tag &key, uint seed = 0)
+{
+ return qHash(quint32(key), seed);
+}
+
+namespace QIcc {
+
+struct TagTableEntry
+{
+ quint32_be signature;
+ quint32_be offset;
+ quint32_be size;
+};
+
+struct GenericTagData {
+ quint32_be type;
+ quint32_be null;
+};
+
+struct XYZTagData : GenericTagData {
+ qint32_be fixedX;
+ qint32_be fixedY;
+ qint32_be fixedZ;
+};
+
+struct CurvTagData : GenericTagData {
+ quint32_be valueCount;
+ quint16_be value[1];
+};
+
+struct ParaTagData : GenericTagData {
+ quint16_be curveType;
+ quint16_be null2;
+ quint32_be parameter[1];
+};
+
+// For both mAB and mBA
+struct mABTagData : GenericTagData {
+ quint8 inputChannels;
+ quint8 outputChannels;
+ quint8 padding[2];
+ quint32_be bCurvesOffset;
+ quint32_be matrixOffset;
+ quint32_be mCurvesOffset;
+ quint32_be clutOffset;
+ quint32_be aCurvesOffset;
+};
+
+struct Sf32TagData : GenericTagData {
+ quint32_be value[1];
+};
+
+static int toFixedS1516(float x)
+{
+ return int(x * 65536.0f + 0.5f);
+}
+
+static float fromFixedS1516(int x)
+{
+ return x * (1.0f / 65536.0f);
+}
+
+QColorVector fromXyzData(const XYZTagData *xyz)
+{
+ const float x = fromFixedS1516(xyz->fixedX);
+ const float y = fromFixedS1516(xyz->fixedY);
+ const float z = fromFixedS1516(xyz->fixedZ);
+ qCDebug(lcIcc) << "XYZ_ " << x << y << z;
+
+ return QColorVector(x, y, z);
+}
+
+static bool isValidIccProfile(const ICCProfileHeader &header)
+{
+ if (header.signature != uint(Tag::acsp)) {
+ qCWarning(lcIcc, "Failed ICC signature test");
+ return false;
+ }
+ if (header.profileSize < (sizeof(ICCProfileHeader) + header.tagCount * sizeof(TagTableEntry))) {
+ qCWarning(lcIcc, "Failed basic size sanity");
+ return false;
+ }
+
+ if (header.profileClass != uint(ProfileClass::Input)
+ && header.profileClass != uint(ProfileClass::Display)) {
+ qCWarning(lcIcc, "Unsupported ICC profile class %x", quint32(header.profileClass));
+ return false;
+ }
+ if (header.inputColorSpace != 0x52474220 /* 'RGB '*/) {
+ qCWarning(lcIcc, "Unsupported ICC input color space %x", quint32(header.inputColorSpace));
+ return false;
+ }
+ if (header.pcs != 0x58595a20 /* 'XYZ '*/) {
+ // ### support PCSLAB
+ qCWarning(lcIcc, "Unsupported ICC profile connection space %x", quint32(header.pcs));
+ return false;
+ }
+
+ QColorVector illuminant;
+ illuminant.x = fromFixedS1516(header.illuminantXyz[0]);
+ illuminant.y = fromFixedS1516(header.illuminantXyz[1]);
+ illuminant.z = fromFixedS1516(header.illuminantXyz[2]);
+ if (illuminant != QColorVector::D50()) {
+ qCWarning(lcIcc, "Invalid ICC illuminant");
+ return false;
+ }
+
+ return true;
+}
+
+static int writeColorTrc(QDataStream &stream, const QColorTrc &trc)
+{
+ if (trc.isLinear()) {
+ stream << uint(Tag::curv) << uint(0);
+ stream << uint(0);
+ return 12;
+ }
+
+ if (trc.m_type == QColorTrc::Type::Function) {
+ const QColorTransferFunction &fun = trc.m_fun;
+ stream << uint(Tag::para) << uint(0);
+ if (fun.isGamma()) {
+ stream << ushort(0) << ushort(0);
+ stream << toFixedS1516(fun.m_g);
+ return 12 + 4;
+ }
+ bool type3 = qFuzzyIsNull(fun.m_e) && qFuzzyIsNull(fun.m_f);
+ stream << ushort(type3 ? 3 : 4) << ushort(0);
+ stream << toFixedS1516(fun.m_g);
+ stream << toFixedS1516(fun.m_a);
+ stream << toFixedS1516(fun.m_b);
+ stream << toFixedS1516(fun.m_c);
+ stream << toFixedS1516(fun.m_d);
+ if (type3)
+ return 12 + 5 * 4;
+ stream << toFixedS1516(fun.m_e);
+ stream << toFixedS1516(fun.m_f);
+ return 12 + 7 * 4;
+ }
+
+ Q_ASSERT(trc.m_type == QColorTrc::Type::Table);
+ stream << uint(Tag::curv) << uint(0);
+ stream << uint(trc.m_table.m_tableSize);
+ if (!trc.m_table.m_table16.isEmpty()) {
+ for (uint i = 0; i < trc.m_table.m_tableSize; ++i) {
+ stream << ushort(trc.m_table.m_table16[i]);
+ }
+ } else {
+ for (uint i = 0; i < trc.m_table.m_tableSize; ++i) {
+ stream << ushort(trc.m_table.m_table8[i] * 257U);
+ }
+ }
+ return 12 + 2 * trc.m_table.m_tableSize;
+}
+
+QByteArray toIccProfile(const QColorSpace &space)
+{
+ if (!space.isValid())
+ return QByteArray();
+
+ const QColorSpacePrivate *spaceDPtr = space.d_func();
+
+ constexpr int tagCount = 9;
+ constexpr uint profileDataOffset = 128 + 4 + 12 * tagCount;
+ constexpr uint variableTagTableOffsets = 128 + 4 + 12 * 5;
+ uint currentOffset = 0;
+ uint rTrcOffset, gTrcOffset, bTrcOffset;
+ uint rTrcSize, gTrcSize, bTrcSize;
+ uint descOffset, descSize;
+
+ QBuffer buffer;
+ buffer.open(QIODevice::WriteOnly);
+ QDataStream stream(&buffer);
+
+ // 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(ProfileClass::Display);
+ stream << uint(Tag::RGB_);
+ stream << uint(Tag::XYZ_);
+ stream << uint(0) << uint(0) << uint(0);
+ stream << uint(Tag::acsp);
+ stream << uint(0) << uint(0) << uint(0);
+ stream << uint(0) << uint(0) << uint(0);
+ stream << uint(1); // Rendering intent
+ stream << uint(0x0000f6d6); // D50 X
+ stream << uint(0x00010000); // D50 Y
+ stream << uint(0x0000d32d); // D50 Z
+ stream << IccTag('Q','t', QT_VERSION_MAJOR, QT_VERSION_MINOR);
+ stream << uint(0) << uint(0) << uint(0) << uint(0);
+ stream << uint(0) << uint(0) << uint(0) << uint(0) << uint(0) << uint(0) << uint(0);
+
+ // Tag table:
+ 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);
+ // 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);
+ stream << toFixedS1516(spaceDPtr->toXyz.r.x);
+ stream << toFixedS1516(spaceDPtr->toXyz.r.y);
+ stream << toFixedS1516(spaceDPtr->toXyz.r.z);
+ stream << uint(Tag::XYZ_) << uint(0);
+ stream << toFixedS1516(spaceDPtr->toXyz.g.x);
+ stream << toFixedS1516(spaceDPtr->toXyz.g.y);
+ stream << toFixedS1516(spaceDPtr->toXyz.g.z);
+ stream << uint(Tag::XYZ_) << uint(0);
+ stream << toFixedS1516(spaceDPtr->toXyz.b.x);
+ stream << toFixedS1516(spaceDPtr->toXyz.b.y);
+ stream << toFixedS1516(spaceDPtr->toXyz.b.z);
+ stream << uint(Tag::XYZ_) << uint(0);
+ 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;
+
+ // From now on the data is variable sized:
+ rTrcOffset = currentOffset;
+ rTrcSize = writeColorTrc(stream, spaceDPtr->trc[0]);
+ currentOffset += rTrcSize;
+ if (spaceDPtr->trc[0] == spaceDPtr->trc[1]) {
+ gTrcOffset = rTrcOffset;
+ gTrcSize = rTrcSize;
+ } else {
+ gTrcOffset = currentOffset;
+ gTrcSize = writeColorTrc(stream, spaceDPtr->trc[1]);
+ currentOffset += gTrcSize;
+ }
+ if (spaceDPtr->trc[0] == spaceDPtr->trc[2]) {
+ bTrcOffset = rTrcOffset;
+ bTrcSize = rTrcSize;
+ } else {
+ bTrcOffset = currentOffset;
+ bTrcSize = writeColorTrc(stream, spaceDPtr->trc[2]);
+ currentOffset += bTrcSize;
+ }
+
+ descOffset = currentOffset;
+ QByteArray description = spaceDPtr->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;
+ currentOffset += descSize;
+
+ buffer.close();
+ QByteArray iccProfile = buffer.buffer();
+ // Now write final size
+ *(quint32_be *)iccProfile.data() = iccProfile.size();
+ // And the final indices and sizes of variable size tags:
+ *(quint32_be *)(iccProfile.data() + variableTagTableOffsets + 4) = rTrcOffset;
+ *(quint32_be *)(iccProfile.data() + variableTagTableOffsets + 8) = rTrcSize;
+ *(quint32_be *)(iccProfile.data() + variableTagTableOffsets + 12 + 4) = gTrcOffset;
+ *(quint32_be *)(iccProfile.data() + variableTagTableOffsets + 12 + 8) = gTrcSize;
+ *(quint32_be *)(iccProfile.data() + variableTagTableOffsets + 2 * 12 + 4) = bTrcOffset;
+ *(quint32_be *)(iccProfile.data() + variableTagTableOffsets + 2 * 12 + 8) = bTrcSize;
+ *(quint32_be *)(iccProfile.data() + variableTagTableOffsets + 3 * 12 + 4) = descOffset;
+ *(quint32_be *)(iccProfile.data() + variableTagTableOffsets + 3 * 12 + 8) = descSize;
+
+#if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS)
+ const ICCProfileHeader *iccHeader = (const ICCProfileHeader *)iccProfile.constData();
+ Q_ASSERT(qsizetype(iccHeader->profileSize) == qsizetype(iccProfile.size()));
+ Q_ASSERT(isValidIccProfile(*iccHeader));
+#endif
+
+ return iccProfile;
+}
+
+bool parseTRC(const GenericTagData *trcData, QColorTrc &gamma)
+{
+ if (trcData->type == quint32(Tag::curv)) {
+ const CurvTagData *curv = reinterpret_cast<const CurvTagData *>(trcData);
+ qCDebug(lcIcc) << "curv" << uint(curv->valueCount);
+ if (curv->valueCount == 0) {
+ gamma.m_type = QColorTrc::Type::Function;
+ gamma.m_fun = QColorTransferFunction(); // Linear
+ } else if (curv->valueCount == 1) {
+ float g = curv->value[0] * (1.0f / 256.0f);
+ qCDebug(lcIcc) << g;
+ gamma.m_type = QColorTrc::Type::Function;
+ gamma.m_fun = QColorTransferFunction::fromGamma(g);
+ } else {
+ QVector<quint16> tabl;
+ tabl.resize(curv->valueCount);
+ for (uint i = 0; i < curv->valueCount; ++i)
+ tabl[i] = curv->value[i];
+ QColorTransferTable table = QColorTransferTable(curv->valueCount, std::move(tabl));
+ QColorTransferFunction curve;
+ if (!table.asColorTransferFunction(&curve)) {
+ gamma.m_type = QColorTrc::Type::Table;
+ gamma.m_table = table;
+ } else {
+ qCDebug(lcIcc) << "Detected curv table as function";
+ gamma.m_type = QColorTrc::Type::Function;
+ gamma.m_fun = curve;
+ }
+ }
+ return true;
+ }
+ if (trcData->type == quint32(Tag::para)) {
+ const ParaTagData *para = reinterpret_cast<const ParaTagData *>(trcData);
+ qCDebug(lcIcc) << "para" << uint(para->curveType);
+ switch (para->curveType) {
+ case 0: {
+ float g = fromFixedS1516(para->parameter[0]);
+ qCDebug(lcIcc) << g;
+ gamma.m_type = QColorTrc::Type::Function;
+ gamma.m_fun = QColorTransferFunction::fromGamma(g);
+ break;
+ }
+ case 1: {
+ float g = fromFixedS1516(para->parameter[0]);
+ float a = fromFixedS1516(para->parameter[1]);
+ float b = fromFixedS1516(para->parameter[2]);
+ float d = -b / a;
+ qCDebug(lcIcc) << g << a << b;
+ gamma.m_type = QColorTrc::Type::Function;
+ gamma.m_fun = QColorTransferFunction(a, b, 0.0f, d, 0.0f, 0.0f, g);
+ break;
+ }
+ case 2: {
+ float g = fromFixedS1516(para->parameter[0]);
+ float a = fromFixedS1516(para->parameter[1]);
+ float b = fromFixedS1516(para->parameter[2]);
+ float c = fromFixedS1516(para->parameter[3]);
+ float d = -b / a;
+ qCDebug(lcIcc) << g << a << b << c;
+ gamma.m_type = QColorTrc::Type::Function;
+ gamma.m_fun = QColorTransferFunction(a, b, 0.0f, d, c, c, g);
+ break;
+ }
+ case 3: {
+ float g = fromFixedS1516(para->parameter[0]);
+ float a = fromFixedS1516(para->parameter[1]);
+ float b = fromFixedS1516(para->parameter[2]);
+ float c = fromFixedS1516(para->parameter[3]);
+ float d = fromFixedS1516(para->parameter[4]);
+ qCDebug(lcIcc) << g << a << b << c << d;
+ gamma.m_type = QColorTrc::Type::Function;
+ gamma.m_fun = QColorTransferFunction(a, b, c, d, 0.0f, 0.0f, g);
+ break;
+ }
+ case 4: {
+ float g = fromFixedS1516(para->parameter[0]);
+ float a = fromFixedS1516(para->parameter[1]);
+ float b = fromFixedS1516(para->parameter[2]);
+ float c = fromFixedS1516(para->parameter[3]);
+ float d = fromFixedS1516(para->parameter[4]);
+ float e = fromFixedS1516(para->parameter[5]);
+ float f = fromFixedS1516(para->parameter[6]);
+ qCDebug(lcIcc) << g << a << b << c << d << e << f;
+ gamma.m_type = QColorTrc::Type::Function;
+ gamma.m_fun = QColorTransferFunction(a, b, c, d, e, f, g);
+ break;
+ }
+ default:
+ qCWarning(lcIcc) << "Unknown para type" << uint(para->curveType);
+ return false;
+ }
+ return true;
+ }
+ qCWarning(lcIcc) << "Invalid TRC data type";
+ return false;
+}
+
+bool fromIccProfile(const QByteArray &data, QColorSpace *colorSpace)
+{
+ if (data.size() < qsizetype(sizeof(ICCProfileHeader))) {
+ qCWarning(lcIcc) << "fromIccProfile: failed size sanity 1";
+ return false;
+ }
+ const ICCProfileHeader *header = (const ICCProfileHeader *)data.constData();
+ if (!isValidIccProfile(*header)) {
+ qCWarning(lcIcc) << "fromIccProfile: failed general sanity check";
+ return false;
+ }
+ if (qsizetype(header->profileSize) > data.size()) {
+ qCWarning(lcIcc) << "fromIccProfile: failed size sanity 2";
+ return false;
+ }
+
+ // Read tag index
+ const TagTableEntry *tagTable = (const TagTableEntry *)(data.constData() + sizeof(ICCProfileHeader));
+ const qsizetype offsetToData = sizeof(ICCProfileHeader) + header->tagCount * sizeof(TagTableEntry);
+
+ QHash<Tag, quint32> tagIndex;
+ for (uint i = 0; i < header->tagCount; ++i) {
+ // Sanity check tag sizes and offsets:
+ if (qsizetype(tagTable[i].offset) < offsetToData) {
+ qCWarning(lcIcc) << "fromIccProfile: failed tag offset sanity 1";
+ return false;
+ }
+ // Checked separately from (+ size) to handle overflow.
+ if (tagTable[i].offset > header->profileSize) {
+ qCWarning(lcIcc) << "fromIccProfile: failed tag offset sanity 2";
+ return false;
+ }
+ if ((tagTable[i].offset + tagTable[i].size) > header->profileSize) {
+ qCWarning(lcIcc) << "fromIccProfile: failed tag offset + size sanity";
+ return false;
+ }
+// printf("'%4s' %d %d\n", (const char *)&tagTable[i].signature,
+// quint32(tagTable[i].offset),
+// quint32(tagTable[i].size));
+ tagIndex.insert(Tag(quint32(tagTable[i].signature)), tagTable[i].offset);
+ }
+ // Check the profile is three-component matrix based (what we currently support):
+ 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)) {
+ qCWarning(lcIcc) << "fromIccProfile: Unsupported ICC profile - not three component matrix based";
+ return false;
+ }
+
+ // Parse XYZ tags
+ const XYZTagData *rXyz = (const XYZTagData *)(data.constData() + tagIndex[Tag::rXYZ]);
+ const XYZTagData *gXyz = (const XYZTagData *)(data.constData() + tagIndex[Tag::gXYZ]);
+ const XYZTagData *bXyz = (const XYZTagData *)(data.constData() + tagIndex[Tag::bXYZ]);
+ const XYZTagData *wXyz = (const XYZTagData *)(data.constData() + tagIndex[Tag::wtpt]);
+ if (rXyz->type != quint32(Tag::XYZ_) || gXyz->type != quint32(Tag::XYZ_) ||
+ wXyz->type != quint32(Tag::XYZ_) || wXyz->type != quint32(Tag::XYZ_)) {
+ qCWarning(lcIcc) << "fromIccProfile: Bad XYZ data type";
+ return false;
+ }
+ QColorSpacePrivate *colorspaceDPtr = colorSpace->d_func();
+
+ colorspaceDPtr->toXyz.r = fromXyzData(rXyz);
+ colorspaceDPtr->toXyz.g = fromXyzData(gXyz);
+ colorspaceDPtr->toXyz.b = fromXyzData(bXyz);
+ QColorVector whitePoint = fromXyzData(wXyz);
+ colorspaceDPtr->whitePoint = whitePoint;
+
+ colorspaceDPtr->gamut = QColorSpace::Gamut::Custom;
+ if (colorspaceDPtr->toXyz == QColorMatrix::toXyzFromSRgb()) {
+ qCDebug(lcIcc) << "fromIccProfile: sRGB gamut detected";
+ colorspaceDPtr->gamut = QColorSpace::Gamut::SRgb;
+ } else if (colorspaceDPtr->toXyz == QColorMatrix::toXyzFromAdobeRgb()) {
+ qCDebug(lcIcc) << "fromIccProfile: Adobe RGB gamut detected";
+ colorspaceDPtr->gamut = QColorSpace::Gamut::AdobeRgb;
+ } else if (colorspaceDPtr->toXyz == QColorMatrix::toXyzFromDciP3D65()) {
+ qCDebug(lcIcc) << "fromIccProfile: DCI-P3 D65 gamut detected";
+ colorspaceDPtr->gamut = QColorSpace::Gamut::DciP3D65;
+ } else if (colorspaceDPtr->toXyz == QColorMatrix::toXyzFromBt2020()) {
+ qCDebug(lcIcc) << "fromIccProfile: BT.2020 gamut detected";
+ colorspaceDPtr->gamut = QColorSpace::Gamut::Bt2020;
+ }
+ if (colorspaceDPtr->toXyz == QColorMatrix::toXyzFromProPhotoRgb()) {
+ qCDebug(lcIcc) << "fromIccProfile: ProPhoto RGB gamut detected";
+ colorspaceDPtr->gamut = QColorSpace::Gamut::ProPhotoRgb;
+ }
+ // Reset the matrix to our canonical values:
+ if (colorspaceDPtr->gamut != QColorSpace::Gamut::Custom)
+ colorspaceDPtr->setToXyzMatrix();
+
+ // Parse TRC tags
+ const GenericTagData *rTrc;
+ const GenericTagData *gTrc;
+ const GenericTagData *bTrc;
+ if (tagIndex.contains(Tag::aarg) && tagIndex.contains(Tag::aagg) && tagIndex.contains(Tag::aabg)) {
+ // Apple extension for parametric version of TRCs in ICCv2:
+ rTrc = (const GenericTagData *)(data.constData() + tagIndex[Tag::aarg]);
+ gTrc = (const GenericTagData *)(data.constData() + tagIndex[Tag::aagg]);
+ bTrc = (const GenericTagData *)(data.constData() + tagIndex[Tag::aabg]);
+ } else {
+ rTrc = (const GenericTagData *)(data.constData() + tagIndex[Tag::rTRC]);
+ gTrc = (const GenericTagData *)(data.constData() + tagIndex[Tag::gTRC]);
+ bTrc = (const GenericTagData *)(data.constData() + tagIndex[Tag::bTRC]);
+ }
+
+ QColorTrc rCurve;
+ QColorTrc gCurve;
+ QColorTrc bCurve;
+ if (!parseTRC(rTrc, rCurve))
+ return false;
+ if (!parseTRC(gTrc, gCurve))
+ return false;
+ if (!parseTRC(bTrc, bCurve))
+ 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;
+ }
+
+ 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;
+ }
+
+ // FIXME: try to parse the description..
+
+ if (!colorspaceDPtr->identifyColorSpace())
+ colorspaceDPtr->id = QColorSpace::Unknown;
+ else
+ qCDebug(lcIcc) << "fromIccProfile: Named colorspace detected: " << colorSpace->colorSpaceId();
+
+ colorspaceDPtr->iccProfile = data;
+
+ return true;
+}
+
+} // namespace QIcc
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qicc_p.h b/src/gui/painting/qicc_p.h
new file mode 100644
index 0000000000..c3220391f4
--- /dev/null
+++ b/src/gui/painting/qicc_p.h
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QICC_P_H
+#define QICC_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/qbytearray.h>
+#include <QtGui/qtguiglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+class QColorSpace;
+
+namespace QIcc {
+
+Q_GUI_EXPORT bool fromIccProfile(const QByteArray &data, QColorSpace *colorSpace);
+Q_GUI_EXPORT QByteArray toIccProfile(const QColorSpace &space);
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QICC_P_H
diff --git a/src/gui/painting/qimagescale.cpp b/src/gui/painting/qimagescale.cpp
index 8a5274bd37..0d7205b483 100644
--- a/src/gui/painting/qimagescale.cpp
+++ b/src/gui/painting/qimagescale.cpp
@@ -528,6 +528,7 @@ static void qt_qimageScaleAARGBA_down_xy(QImageScaleInfo *isi, unsigned int *des
}
}
+#if QT_CONFIG(raster_64bit)
static void qt_qimageScaleRgba64_up_x_down_y(QImageScaleInfo *isi, QRgba64 *dest,
int dw, int dh, int dow, int sow);
@@ -728,6 +729,7 @@ static void qt_qimageScaleRgba64_down_xy(QImageScaleInfo *isi, QRgba64 *dest,
}
}
}
+#endif
static void qt_qimageScaleAARGB_up_x_down_y(QImageScaleInfo *isi, unsigned int *dest,
int dw, int dh, int dow, int sow);
@@ -945,10 +947,13 @@ QImage qSmoothScaleImage(const QImage &src, int dw, int dh)
return QImage();
}
+#if QT_CONFIG(raster_64bit)
if (src.depth() > 32)
qt_qimageScaleRgba64(scaleinfo, (QRgba64 *)buffer.scanLine(0),
dw, dh, dw, src.bytesPerLine() / 8);
- else if (src.hasAlphaChannel())
+ else
+#endif
+ if (src.hasAlphaChannel())
qt_qimageScaleAARGBA(scaleinfo, (unsigned int *)buffer.scanLine(0),
dw, dh, dw, src.bytesPerLine() / 4);
else
diff --git a/src/gui/painting/qmatrix.cpp b/src/gui/painting/qmatrix.cpp
index 24b33243da..b1f01332b6 100644
--- a/src/gui/painting/qmatrix.cpp
+++ b/src/gui/painting/qmatrix.cpp
@@ -248,7 +248,7 @@ QMatrix::QMatrix(qreal m11, qreal m12, qreal m21, qreal m22, qreal dx, qreal dy)
/*!
Constructs a matrix that is a copy of the given \a matrix.
*/
-QMatrix::QMatrix(const QMatrix &matrix) Q_DECL_NOTHROW
+QMatrix::QMatrix(const QMatrix &matrix) noexcept
: _m11(matrix._m11)
, _m12(matrix._m12)
, _m21(matrix._m21)
@@ -989,7 +989,7 @@ bool QMatrix::operator==(const QMatrix &m) const
Returns the hash value for \a key, using
\a seed to seed the calculation.
*/
-uint qHash(const QMatrix &key, uint seed) Q_DECL_NOTHROW
+uint qHash(const QMatrix &key, uint seed) noexcept
{
QtPrivate::QHashCombine hash;
seed = hash(seed, key.m11());
@@ -1068,7 +1068,7 @@ QMatrix QMatrix::operator *(const QMatrix &m) const
/*!
Assigns the given \a matrix's values to this matrix.
*/
-QMatrix &QMatrix::operator=(const QMatrix &matrix) Q_DECL_NOTHROW
+QMatrix &QMatrix::operator=(const QMatrix &matrix) noexcept
{
_m11 = matrix._m11;
_m12 = matrix._m12;
diff --git a/src/gui/painting/qmatrix.h b/src/gui/painting/qmatrix.h
index d8a4fcfb1c..a167260ade 100644
--- a/src/gui/painting/qmatrix.h
+++ b/src/gui/painting/qmatrix.h
@@ -64,12 +64,12 @@ public:
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
// ### Qt 6: remove; the compiler-generated ones are fine!
- QMatrix &operator=(QMatrix &&other) Q_DECL_NOTHROW // = default
+ QMatrix &operator=(QMatrix &&other) noexcept // = default
{ memcpy(static_cast<void *>(this), static_cast<void *>(&other), sizeof(QMatrix)); return *this; }
- QMatrix &operator=(const QMatrix &) Q_DECL_NOTHROW; // = default
- QMatrix(QMatrix &&other) Q_DECL_NOTHROW // = default
+ QMatrix &operator=(const QMatrix &) noexcept; // = default
+ QMatrix(QMatrix &&other) noexcept // = default
{ memcpy(static_cast<void *>(this), static_cast<void *>(&other), sizeof(QMatrix)); }
- QMatrix(const QMatrix &other) Q_DECL_NOTHROW; // = default
+ QMatrix(const QMatrix &other) noexcept; // = default
#endif
void setMatrix(qreal m11, qreal m12, qreal m21, qreal m22,
@@ -140,7 +140,7 @@ private:
};
Q_DECLARE_TYPEINFO(QMatrix, Q_MOVABLE_TYPE);
-Q_GUI_EXPORT Q_DECL_CONST_FUNCTION uint qHash(const QMatrix &key, uint seed = 0) Q_DECL_NOTHROW;
+Q_GUI_EXPORT Q_DECL_CONST_FUNCTION uint qHash(const QMatrix &key, uint seed = 0) noexcept;
// mathematical semantics
inline QPoint operator*(const QPoint &p, const QMatrix &m)
diff --git a/src/gui/painting/qoutlinemapper.cpp b/src/gui/painting/qoutlinemapper.cpp
index b2d02182c3..2074f98069 100644
--- a/src/gui/painting/qoutlinemapper.cpp
+++ b/src/gui/painting/qoutlinemapper.cpp
@@ -42,6 +42,7 @@
#include "qbezier_p.h"
#include "qmath.h"
#include "qpainterpath_p.h"
+#include "qscopedvaluerollback.h"
#include <stdlib.h>
@@ -354,7 +355,7 @@ void QOutlineMapper::clipElements(const QPointF *elements,
// instead of going through convenience functionallity, but since
// this part of code hardly every used, it shouldn't matter.
- m_in_clip_elements = true;
+ QScopedValueRollback<bool> in_clip_elements(m_in_clip_elements, true);
QPainterPath path;
@@ -397,8 +398,6 @@ void QOutlineMapper::clipElements(const QPointF *elements,
convertPath(clippedPath);
m_transform = oldTransform;
}
-
- m_in_clip_elements = false;
}
QT_END_NAMESPACE
diff --git a/src/gui/painting/qoutlinemapper_p.h b/src/gui/painting/qoutlinemapper_p.h
index 71999fbdee..04a68797c2 100644
--- a/src/gui/painting/qoutlinemapper_p.h
+++ b/src/gui/painting/qoutlinemapper_p.h
@@ -180,13 +180,13 @@ public:
QT_FT_Outline *outline() {
if (m_valid)
return &m_outline;
- return 0;
+ return nullptr;
}
QT_FT_Outline *convertPath(const QPainterPath &path);
QT_FT_Outline *convertPath(const QVectorPath &path);
- inline QPainterPath::ElementType *elementTypes() const { return m_element_types.size() == 0 ? 0 : m_element_types.data(); }
+ inline QPainterPath::ElementType *elementTypes() const { return m_element_types.size() == 0 ? nullptr : m_element_types.data(); }
public:
QDataBuffer<QPainterPath::ElementType> m_element_types;
diff --git a/src/gui/painting/qpagelayout.h b/src/gui/painting/qpagelayout.h
index b41689d33b..7ee0ce7a76 100644
--- a/src/gui/painting/qpagelayout.h
+++ b/src/gui/painting/qpagelayout.h
@@ -82,13 +82,11 @@ public:
const QMarginsF &margins, Unit units = Point,
const QMarginsF &minMargins = QMarginsF(0, 0, 0, 0));
QPageLayout(const QPageLayout &other);
-#ifdef Q_COMPILER_RVALUE_REFS
- QPageLayout &operator=(QPageLayout &&other) Q_DECL_NOTHROW { swap(other); return *this; }
-#endif
+ QPageLayout &operator=(QPageLayout &&other) noexcept { swap(other); return *this; }
QPageLayout &operator=(const QPageLayout &other);
~QPageLayout();
- void swap(QPageLayout &other) Q_DECL_NOTHROW { qSwap(d, other.d); }
+ void swap(QPageLayout &other) noexcept { qSwap(d, other.d); }
friend Q_GUI_EXPORT bool operator==(const QPageLayout &lhs, const QPageLayout &rhs);
bool isEquivalentTo(const QPageLayout &other) const;
diff --git a/src/gui/painting/qpagesize.h b/src/gui/painting/qpagesize.h
index 82054824b4..133274760f 100644
--- a/src/gui/painting/qpagesize.h
+++ b/src/gui/painting/qpagesize.h
@@ -236,14 +236,12 @@ public:
const QString &name = QString(),
SizeMatchPolicy matchPolicy = FuzzyMatch);
QPageSize(const QPageSize &other);
-#ifdef Q_COMPILER_RVALUE_REFS
- QPageSize &operator=(QPageSize &&other) Q_DECL_NOTHROW { swap(other); return *this; }
-#endif
+ QPageSize &operator=(QPageSize &&other) noexcept { swap(other); return *this; }
QPageSize &operator=(const QPageSize &other);
~QPageSize();
- void swap(QPageSize &other) Q_DECL_NOTHROW { qSwap(d, other.d); }
+ void swap(QPageSize &other) noexcept { qSwap(d, other.d); }
friend Q_GUI_EXPORT bool operator==(const QPageSize &lhs, const QPageSize &rhs);
bool isEquivalentTo(const QPageSize &other) const;
diff --git a/src/gui/painting/qpaintdevice.cpp b/src/gui/painting/qpaintdevice.cpp
index faaa316085..0ddfba6ee9 100644
--- a/src/gui/painting/qpaintdevice.cpp
+++ b/src/gui/painting/qpaintdevice.cpp
@@ -41,7 +41,7 @@
QT_BEGIN_NAMESPACE
-QPaintDevice::QPaintDevice() Q_DECL_NOEXCEPT
+QPaintDevice::QPaintDevice() noexcept
{
reserved = 0;
painters = 0;
diff --git a/src/gui/painting/qpaintdevice.h b/src/gui/painting/qpaintdevice.h
index 9458b4ba9a..5f8dad205d 100644
--- a/src/gui/painting/qpaintdevice.h
+++ b/src/gui/painting/qpaintdevice.h
@@ -90,7 +90,7 @@ public:
static inline qreal devicePixelRatioFScale() { return 0x10000; }
protected:
- QPaintDevice() Q_DECL_NOEXCEPT;
+ QPaintDevice() noexcept;
virtual int metric(PaintDeviceMetric metric) const;
virtual void initPainter(QPainter *painter) const;
virtual QPaintDevice *redirected(QPoint *offset) const;
diff --git a/src/gui/painting/qpaintengine_p.h b/src/gui/painting/qpaintengine_p.h
index 8ac3fcff5c..40b9474165 100644
--- a/src/gui/painting/qpaintengine_p.h
+++ b/src/gui/painting/qpaintengine_p.h
@@ -65,7 +65,7 @@ class Q_GUI_EXPORT QPaintEnginePrivate
{
Q_DECLARE_PUBLIC(QPaintEngine)
public:
- QPaintEnginePrivate() : pdev(0), q_ptr(0), currentClipDevice(0), hasSystemTransform(0),
+ QPaintEnginePrivate() : pdev(nullptr), q_ptr(nullptr), currentClipDevice(nullptr), hasSystemTransform(0),
hasSystemViewport(0) {}
virtual ~QPaintEnginePrivate();
@@ -138,8 +138,8 @@ public:
static QPaintEnginePrivate *get(QPaintEngine *paintEngine) { return paintEngine->d_func(); }
- virtual QPaintEngine *aggregateEngine() { return 0; }
- virtual Qt::HANDLE nativeHandle() { return 0; }
+ virtual QPaintEngine *aggregateEngine() { return nullptr; }
+ virtual Qt::HANDLE nativeHandle() { return nullptr; }
};
QT_END_NAMESPACE
diff --git a/src/gui/painting/qpaintengine_raster.cpp b/src/gui/painting/qpaintengine_raster.cpp
index 65319c0804..85ddff53db 100644
--- a/src/gui/painting/qpaintengine_raster.cpp
+++ b/src/gui/painting/qpaintengine_raster.cpp
@@ -555,35 +555,6 @@ bool QRasterPaintEngine::end()
/*!
\internal
*/
-void QRasterPaintEngine::releaseBuffer()
-{
- Q_D(QRasterPaintEngine);
- d->rasterBuffer.reset(new QRasterBuffer);
-}
-
-/*!
- \internal
-*/
-QSize QRasterPaintEngine::size() const
-{
- Q_D(const QRasterPaintEngine);
- return QSize(d->rasterBuffer->width(), d->rasterBuffer->height());
-}
-
-/*!
- \internal
-*/
-#ifndef QT_NO_DEBUG
-void QRasterPaintEngine::saveBuffer(const QString &s) const
-{
- Q_D(const QRasterPaintEngine);
- d->rasterBuffer->bufferImage().save(s, "PNG");
-}
-#endif
-
-/*!
- \internal
-*/
void QRasterPaintEngine::updateMatrix(const QTransform &matrix)
{
QRasterPaintEngineState *s = state();
@@ -927,7 +898,7 @@ void QRasterPaintEngine::renderHintsChanged()
QRasterPaintEngineState *s = state();
#ifdef QT_DEBUG_DRAW
- qDebug() << "QRasterPaintEngine::renderHintsChanged()" << hex << s->renderHints;
+ qDebug() << "QRasterPaintEngine::renderHintsChanged()" << Qt::hex << s->renderHints;
#endif
bool was_aa = s->flags.antialiased;
@@ -1774,7 +1745,7 @@ void QRasterPaintEngine::fill(const QVectorPath &path, const QBrush &brush)
QRectF rf = path.controlPointRect();
qDebug() << "QRasterPaintEngine::fill(): "
<< "size=" << path.elementCount()
- << ", hints=" << hex << path.hints()
+ << ", hints=" << Qt::hex << path.hints()
<< rf << brush;
#endif
@@ -3845,11 +3816,6 @@ QImage::Format QRasterBuffer::prepare(QImage *image)
return format;
}
-void QRasterBuffer::resetBuffer(int val)
-{
- memset(m_buffer, val, m_height*bytes_per_line);
-}
-
QClipData::QClipData(int height)
{
clipSpanHeight = height;
@@ -4272,55 +4238,13 @@ static void qt_span_clip(int count, const QSpan *spans, void *userData)
}
}
-#ifndef QT_NO_DEBUG
-QImage QRasterBuffer::bufferImage() const
-{
- QImage image(m_width, m_height, QImage::Format_ARGB32_Premultiplied);
-
- for (int y = 0; y < m_height; ++y) {
- uint *span = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
-
- for (int x=0; x<m_width; ++x) {
- uint argb = span[x];
- image.setPixel(x, y, argb);
- }
- }
- return image;
-}
-#endif
-
-
-void QRasterBuffer::flushToARGBImage(QImage *target) const
-{
- int w = qMin(m_width, target->width());
- int h = qMin(m_height, target->height());
-
- for (int y=0; y<h; ++y) {
- uint *sourceLine = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
- QRgb *dest = (QRgb *) target->scanLine(y);
- for (int x=0; x<w; ++x) {
- QRgb pixel = sourceLine[x];
- int alpha = qAlpha(pixel);
- if (!alpha) {
- dest[x] = 0;
- } else {
- dest[x] = (alpha << 24)
- | ((255*qRed(pixel)/alpha) << 16)
- | ((255*qGreen(pixel)/alpha) << 8)
- | ((255*qBlue(pixel)/alpha) << 0);
- }
- }
- }
-}
-
-
class QGradientCache
{
public:
struct CacheInfo : QSpanData::Pinnable
{
inline CacheInfo(QGradientStops s, int op, QGradient::InterpolationMode mode) :
- stops(qMove(s)), opacity(op), interpolationMode(mode) {}
+ stops(std::move(s)), opacity(op), interpolationMode(mode) {}
QRgba64 buffer64[GRADIENT_STOPTABLE_SIZE];
QRgb buffer32[GRADIENT_STOPTABLE_SIZE];
QGradientStops stops;
@@ -4607,7 +4531,9 @@ void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode
auto cacheInfo = qt_gradient_cache()->getBuffer(*g, alpha);
gradient.colorTable32 = cacheInfo->buffer32;
+#if QT_CONFIG(raster_64bit)
gradient.colorTable64 = cacheInfo->buffer64;
+#endif
cachedGradient = std::move(cacheInfo);
gradient.spread = g->spread();
@@ -4629,7 +4555,9 @@ void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode
auto cacheInfo = qt_gradient_cache()->getBuffer(*g, alpha);
gradient.colorTable32 = cacheInfo->buffer32;
+#if QT_CONFIG(raster_64bit)
gradient.colorTable64 = cacheInfo->buffer64;
+#endif
cachedGradient = std::move(cacheInfo);
gradient.spread = g->spread();
@@ -4655,7 +4583,9 @@ void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode
auto cacheInfo = qt_gradient_cache()->getBuffer(*g, alpha);
gradient.colorTable32 = cacheInfo->buffer32;
+#if QT_CONFIG(raster_64bit)
gradient.colorTable64 = cacheInfo->buffer64;
+#endif
cachedGradient = std::move(cacheInfo);
gradient.spread = QGradient::RepeatSpread;
diff --git a/src/gui/painting/qpaintengine_raster_p.h b/src/gui/painting/qpaintengine_raster_p.h
index 5d527207a4..ec4a35087a 100644
--- a/src/gui/painting/qpaintengine_raster_p.h
+++ b/src/gui/painting/qpaintengine_raster_p.h
@@ -208,15 +208,6 @@ public:
ClipType clipType() const;
QRect clipBoundingRect() const;
- void releaseBuffer();
-
- QSize size() const;
-
-#ifndef QT_NO_DEBUG
- void saveBuffer(const QString &s) const;
-#endif
-
-
#ifdef Q_OS_WIN
void setDC(HDC hdc);
HDC getDC() const;
@@ -435,27 +426,16 @@ inline void QClipData::appendSpans(const QSpan *s, int num)
class QRasterBuffer
{
public:
- QRasterBuffer() : m_width(0), m_height(0), m_buffer(0) { init(); }
+ QRasterBuffer() : m_width(0), m_height(0), m_buffer(nullptr) { init(); }
~QRasterBuffer();
void init();
QImage::Format prepare(QImage *image);
- QImage::Format prepare(QPixmap *pix);
- void prepare(int w, int h);
- void prepareBuffer(int w, int h);
-
- void resetBuffer(int val=0);
uchar *scanLine(int y) { Q_ASSERT(y>=0); Q_ASSERT(y<m_height); return m_buffer + y * qsizetype(bytes_per_line); }
-#ifndef QT_NO_DEBUG
- QImage bufferImage() const;
-#endif
-
- void flushToARGBImage(QImage *image) const;
-
int width() const { return m_width; }
int height() const { return m_height; }
int bytesPerLine() const { return bytes_per_line; }
diff --git a/src/gui/painting/qpaintengineex.cpp b/src/gui/painting/qpaintengineex.cpp
index 22d3fb3001..8314e8bc8a 100644
--- a/src/gui/painting/qpaintengineex.cpp
+++ b/src/gui/painting/qpaintengineex.cpp
@@ -140,7 +140,7 @@ QDebug Q_GUI_EXPORT &operator<<(QDebug &s, const QVectorPath &path)
QDebugStateSaver saver(s);
QRectF rf = path.controlPointRect();
s << "QVectorPath(size:" << path.elementCount()
- << " hints:" << hex << path.hints()
+ << " hints:" << Qt::hex << path.hints()
<< rf << ')';
return s;
}
diff --git a/src/gui/painting/qpainter_p.h b/src/gui/painting/qpainter_p.h
index 930180e9fa..bd2bc4c9b3 100644
--- a/src/gui/painting/qpainter_p.h
+++ b/src/gui/painting/qpainter_p.h
@@ -54,6 +54,8 @@
#include <QtCore/qvarlengtharray.h>
#include <QtGui/private/qtguiglobal_p.h>
#include "QtGui/qbrush.h"
+#include "QtGui/qcolorspace.h"
+#include "QtGui/qcolortransform.h"
#include "QtGui/qfont.h"
#include "QtGui/qpen.h"
#include "QtGui/qregion.h"
@@ -191,9 +193,9 @@ class QPainterPrivate
Q_DECLARE_PUBLIC(QPainter)
public:
QPainterPrivate(QPainter *painter)
- : q_ptr(painter), d_ptrs(0), state(0), dummyState(0), txinv(0), inDestructor(false), d_ptrs_size(0),
- refcount(1), device(0), original_device(0), helper_device(0), engine(0), emulationEngine(0),
- extended(0)
+ : q_ptr(painter), d_ptrs(nullptr), state(nullptr), dummyState(nullptr), txinv(0), inDestructor(false), d_ptrs_size(0),
+ refcount(1), device(nullptr), original_device(nullptr), helper_device(nullptr), engine(nullptr), emulationEngine(nullptr),
+ extended(nullptr)
{
}
diff --git a/src/gui/painting/qpainterpath.cpp b/src/gui/painting/qpainterpath.cpp
index cbe34c2857..a0db35ebd3 100644
--- a/src/gui/painting/qpainterpath.cpp
+++ b/src/gui/painting/qpainterpath.cpp
@@ -93,7 +93,7 @@ struct QPainterPathPrivateDeleter
{
static inline void cleanup(QPainterPathPrivate *d)
{
- // note - we must up-cast to QPainterPathData since QPainterPathPrivate
+ // note - we must downcast to QPainterPathData since QPainterPathPrivate
// has a non-virtual destructor!
if (d && !d->ref.deref())
delete static_cast<QPainterPathData *>(d);
@@ -545,7 +545,7 @@ void QPainterPath::setElementPositionAt(int i, qreal x, qreal y)
/*!
Constructs an empty QPainterPath object.
*/
-QPainterPath::QPainterPath() Q_DECL_NOEXCEPT
+QPainterPath::QPainterPath() noexcept
: d_ptr(0)
{
}
@@ -3518,8 +3518,7 @@ void QPainterPath::setDirty(bool dirty)
{
d_func()->dirtyBounds = dirty;
d_func()->dirtyControlBounds = dirty;
- delete d_func()->pathConverter;
- d_func()->pathConverter = 0;
+ d_func()->pathConverter.reset();
d_func()->convex = false;
}
@@ -3595,10 +3594,10 @@ void QPainterPath::computeControlPointRect() const
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug s, const QPainterPath &p)
{
- s.nospace() << "QPainterPath: Element count=" << p.elementCount() << endl;
+ s.nospace() << "QPainterPath: Element count=" << p.elementCount() << Qt::endl;
const char *types[] = {"MoveTo", "LineTo", "CurveTo", "CurveToData"};
for (int i=0; i<p.elementCount(); ++i) {
- s.nospace() << " -> " << types[p.elementAt(i).type] << "(x=" << p.elementAt(i).x << ", y=" << p.elementAt(i).y << ')' << endl;
+ s.nospace() << " -> " << types[p.elementAt(i).type] << "(x=" << p.elementAt(i).x << ", y=" << p.elementAt(i).y << ')' << Qt::endl;
}
return s;
diff --git a/src/gui/painting/qpainterpath.h b/src/gui/painting/qpainterpath.h
index a69a192767..ed5be667b7 100644
--- a/src/gui/painting/qpainterpath.h
+++ b/src/gui/painting/qpainterpath.h
@@ -88,17 +88,15 @@ public:
inline bool operator!=(const Element &e) const { return !operator==(e); }
};
- QPainterPath() Q_DECL_NOEXCEPT;
+ QPainterPath() noexcept;
explicit QPainterPath(const QPointF &startPoint);
QPainterPath(const QPainterPath &other);
QPainterPath &operator=(const QPainterPath &other);
-#ifdef Q_COMPILER_RVALUE_REFS
- inline QPainterPath &operator=(QPainterPath &&other) Q_DECL_NOEXCEPT
+ inline QPainterPath &operator=(QPainterPath &&other) noexcept
{ qSwap(d_ptr, other.d_ptr); return *this; }
-#endif
~QPainterPath();
- inline void swap(QPainterPath &other) Q_DECL_NOEXCEPT { d_ptr.swap(other.d_ptr); }
+ inline void swap(QPainterPath &other) noexcept { d_ptr.swap(other.d_ptr); }
void clear();
void reserve(int size);
diff --git a/src/gui/painting/qpainterpath_p.h b/src/gui/painting/qpainterpath_p.h
index a36c8005bc..4eb541ec65 100644
--- a/src/gui/painting/qpainterpath_p.h
+++ b/src/gui/painting/qpainterpath_p.h
@@ -62,8 +62,11 @@
#include <private/qvectorpath_p.h>
#include <private/qstroker_p.h>
+#include <memory>
+
QT_BEGIN_NAMESPACE
+// ### Qt 6: merge with QPainterPathData
class QPainterPathPrivate
{
public:
@@ -80,7 +83,19 @@ public:
friend Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QPainterPath &);
#endif
- QPainterPathPrivate() : ref(1) {}
+ QPainterPathPrivate() noexcept
+ : ref(1)
+ {
+ }
+
+ QPainterPathPrivate(const QPainterPathPrivate &other) noexcept
+ : ref(1),
+ elements(other.elements)
+ {
+ }
+
+ QPainterPathPrivate &operator=(const QPainterPathPrivate &) = delete;
+ ~QPainterPathPrivate() = default;
private:
QAtomicInt ref;
@@ -166,30 +181,30 @@ public:
QPainterPathData() :
cStart(0),
fillRule(Qt::OddEvenFill),
+ require_moveTo(false),
dirtyBounds(false),
dirtyControlBounds(false),
- pathConverter(0)
+ convex(false),
+ pathConverter(nullptr)
{
- require_moveTo = false;
- convex = false;
}
QPainterPathData(const QPainterPathData &other) :
- QPainterPathPrivate(), cStart(other.cStart), fillRule(other.fillRule),
+ QPainterPathPrivate(other),
+ cStart(other.cStart),
+ fillRule(other.fillRule),
bounds(other.bounds),
controlBounds(other.controlBounds),
+ require_moveTo(false),
dirtyBounds(other.dirtyBounds),
dirtyControlBounds(other.dirtyControlBounds),
convex(other.convex),
- pathConverter(0)
+ pathConverter(nullptr)
{
- require_moveTo = false;
- elements = other.elements;
}
- ~QPainterPathData() {
- delete pathConverter;
- }
+ QPainterPathData &operator=(const QPainterPathData &) = delete;
+ ~QPainterPathData() = default;
inline bool isClosed() const;
inline void close();
@@ -198,7 +213,7 @@ public:
const QVectorPath &vectorPath() {
if (!pathConverter)
- pathConverter = new QVectorPathConverter(elements, fillRule, convex);
+ pathConverter.reset(new QVectorPathConverter(elements, fillRule, convex));
return pathConverter->path;
}
@@ -213,7 +228,7 @@ public:
uint dirtyControlBounds : 1;
uint convex : 1;
- QVectorPathConverter *pathConverter;
+ std::unique_ptr<QVectorPathConverter> pathConverter;
};
@@ -307,8 +322,7 @@ inline void QPainterPathData::clear()
dirtyControlBounds = false;
convex = false;
- delete pathConverter;
- pathConverter = nullptr;
+ pathConverter.reset();
}
#define KAPPA qreal(0.5522847498)
diff --git a/src/gui/painting/qpathclipper_p.h b/src/gui/painting/qpathclipper_p.h
index c25a479807..9444a87b71 100644
--- a/src/gui/painting/qpathclipper_p.h
+++ b/src/gui/painting/qpathclipper_p.h
@@ -82,7 +82,7 @@ public:
bool intersect();
bool contains();
- static bool pathToRect(const QPainterPath &path, QRectF *rect = 0);
+ static bool pathToRect(const QPainterPath &path, QRectF *rect = nullptr);
static QPainterPath intersect(const QPainterPath &path, const QRectF &rect);
private:
@@ -394,7 +394,7 @@ inline const QPathSegments::Intersection *QPathSegments::intersectionAt(int inde
{
const int intersection = m_segments.at(index).intersection;
if (intersection < 0)
- return 0;
+ return nullptr;
else
return &m_intersections.at(intersection);
}
@@ -428,12 +428,12 @@ inline int QWingedEdge::edgeCount() const
inline QPathEdge *QWingedEdge::edge(int edge)
{
- return edge < 0 ? 0 : &m_edges.at(edge);
+ return edge < 0 ? nullptr : &m_edges.at(edge);
}
inline const QPathEdge *QWingedEdge::edge(int edge) const
{
- return edge < 0 ? 0 : &m_edges.at(edge);
+ return edge < 0 ? nullptr : &m_edges.at(edge);
}
inline int QWingedEdge::vertexCount() const
@@ -449,12 +449,12 @@ inline int QWingedEdge::addVertex(const QPointF &p)
inline QPathVertex *QWingedEdge::vertex(int vertex)
{
- return vertex < 0 ? 0 : &m_vertices.at(vertex);
+ return vertex < 0 ? nullptr : &m_vertices.at(vertex);
}
inline const QPathVertex *QWingedEdge::vertex(int vertex) const
{
- return vertex < 0 ? 0 : &m_vertices.at(vertex);
+ return vertex < 0 ? nullptr : &m_vertices.at(vertex);
}
inline QPathEdge::Traversal QWingedEdge::flip(QPathEdge::Traversal traversal)
diff --git a/src/gui/painting/qpdf.cpp b/src/gui/painting/qpdf.cpp
index 6bdc82a8e9..25d488961c 100644
--- a/src/gui/painting/qpdf.cpp
+++ b/src/gui/painting/qpdf.cpp
@@ -1671,19 +1671,19 @@ int QPdfEnginePrivate::writeXmpMetaData()
const QDateTime now = QDateTime::currentDateTime();
const QDate date = now.date();
const QTime time = now.time();
-
- QString timeStr;
- timeStr.sprintf("%d-%02d-%02dT%02d:%02d:%02d", date.year(), date.month(), date.day(),
- time.hour(), time.minute(), time.second());
+ const QString timeStr =
+ QString::asprintf("%d-%02d-%02dT%02d:%02d:%02d",
+ date.year(), date.month(), date.day(),
+ time.hour(), time.minute(), time.second());
const int offset = now.offsetFromUtc();
const int hours = (offset / 60) / 60;
const int mins = (offset / 60) % 60;
QString tzStr;
if (offset < 0)
- tzStr.sprintf("-%02d:%02d", -hours, -mins);
+ tzStr = QString::asprintf("-%02d:%02d", -hours, -mins);
else if (offset > 0)
- tzStr.sprintf("+%02d:%02d", hours , mins);
+ tzStr = QString::asprintf("+%02d:%02d", hours , mins);
else
tzStr = QLatin1String("Z");
diff --git a/src/gui/painting/qpdfwriter.cpp b/src/gui/painting/qpdfwriter.cpp
index 258939a763..7f18ce42be 100644
--- a/src/gui/painting/qpdfwriter.cpp
+++ b/src/gui/painting/qpdfwriter.cpp
@@ -379,6 +379,9 @@ int QPdfWriter::resolution() const
*/
#endif
+#if QT_DEPRECATED_SINCE(5, 14)
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_DEPRECATED
/*!
\reimp
@@ -404,6 +407,8 @@ void QPdfWriter::setPageSizeMM(const QSizeF &size)
{
setPageSize(QPageSize(size, QPageSize::Millimeter));
}
+QT_WARNING_POP
+#endif
/*!
\internal
@@ -427,6 +432,9 @@ bool QPdfWriter::newPage()
}
+#if QT_DEPRECATED_SINCE(5, 14)
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_DEPRECATED
/*!
\reimp
@@ -438,6 +446,8 @@ void QPdfWriter::setMargins(const Margins &m)
{
setPageMargins(QMarginsF(m.left, m.top, m.right, m.bottom), QPageLayout::Millimeter);
}
+QT_WARNING_POP
+#endif
QT_END_NAMESPACE
diff --git a/src/gui/painting/qpdfwriter.h b/src/gui/painting/qpdfwriter.h
index b260805b2b..668081e008 100644
--- a/src/gui/painting/qpdfwriter.h
+++ b/src/gui/painting/qpdfwriter.h
@@ -86,10 +86,14 @@ public:
using QPagedPaintDevice::setPageSize;
#endif
+#if QT_DEPRECATED_SINCE(5, 14)
+ QT_DEPRECATED_X("Use setPageSize(QPageSize(id)) instead")
void setPageSize(PageSize size) override;
+ QT_DEPRECATED_X("Use setPageSize(QPageSize(size, QPageSize::Millimeter)) instead")
void setPageSizeMM(const QSizeF &size) override;
-
+ QT_DEPRECATED_X("Use setPageMargins(QMarginsF(l, t, r, b), QPageLayout::Millimeter) instead")
void setMargins(const Margins &m) override;
+#endif
protected:
QPaintEngine *paintEngine() const override;
diff --git a/src/gui/painting/qpen.cpp b/src/gui/painting/qpen.cpp
index 778c770b82..58a1716037 100644
--- a/src/gui/painting/qpen.cpp
+++ b/src/gui/painting/qpen.cpp
@@ -322,7 +322,7 @@ QPen::QPen(const QBrush &brush, qreal width, Qt::PenStyle s, Qt::PenCapStyle c,
Constructs a pen that is a copy of the given \a pen.
*/
-QPen::QPen(const QPen &p) Q_DECL_NOTHROW
+QPen::QPen(const QPen &p) noexcept
{
d = p.d;
if (d)
@@ -381,7 +381,7 @@ void QPen::detach()
this pen.
*/
-QPen &QPen::operator=(const QPen &p) Q_DECL_NOTHROW
+QPen &QPen::operator=(const QPen &p) noexcept
{
QPen(p).swap(*this);
return *this;
diff --git a/src/gui/painting/qpen.h b/src/gui/painting/qpen.h
index 03abfb3d7d..10b11d1d85 100644
--- a/src/gui/painting/qpen.h
+++ b/src/gui/painting/qpen.h
@@ -65,18 +65,16 @@ public:
QPen(const QColor &color);
QPen(const QBrush &brush, qreal width, Qt::PenStyle s = Qt::SolidLine,
Qt::PenCapStyle c = Qt::SquareCap, Qt::PenJoinStyle j = Qt::BevelJoin);
- QPen(const QPen &pen) Q_DECL_NOTHROW;
+ QPen(const QPen &pen) noexcept;
~QPen();
- QPen &operator=(const QPen &pen) Q_DECL_NOTHROW;
-#ifdef Q_COMPILER_RVALUE_REFS
- QPen(QPen &&other) Q_DECL_NOTHROW
+ QPen &operator=(const QPen &pen) noexcept;
+ QPen(QPen &&other) noexcept
: d(other.d) { other.d = nullptr; }
- QPen &operator=(QPen &&other) Q_DECL_NOTHROW
+ QPen &operator=(QPen &&other) noexcept
{ qSwap(d, other.d); return *this; }
-#endif
- void swap(QPen &other) Q_DECL_NOTHROW { qSwap(d, other.d); }
+ void swap(QPen &other) noexcept { qSwap(d, other.d); }
Qt::PenStyle style() const;
void setStyle(Qt::PenStyle);
diff --git a/src/gui/painting/qplatformbackingstore.h b/src/gui/painting/qplatformbackingstore.h
index de5ba964dc..414d2bf0de 100644
--- a/src/gui/painting/qplatformbackingstore.h
+++ b/src/gui/painting/qplatformbackingstore.h
@@ -85,7 +85,7 @@ public:
};
Q_DECLARE_FLAGS(Flags, Flag)
- explicit QPlatformTextureList(QObject *parent = 0);
+ explicit QPlatformTextureList(QObject *parent = nullptr);
~QPlatformTextureList();
int count() const;
@@ -99,7 +99,7 @@ public:
bool isLocked() const;
void appendTexture(void *source, GLuint textureId, const QRect &geometry,
- const QRect &clipRect = QRect(), Flags flags = 0);
+ const QRect &clipRect = QRect(), Flags flags = nullptr);
void clear();
Q_SIGNALS:
diff --git a/src/gui/painting/qpolygon.h b/src/gui/painting/qpolygon.h
index 8e74a499fd..93fab55aa1 100644
--- a/src/gui/painting/qpolygon.h
+++ b/src/gui/painting/qpolygon.h
@@ -60,18 +60,14 @@ public:
inline ~QPolygon() {}
inline explicit QPolygon(int size);
inline /*implicit*/ QPolygon(const QVector<QPoint> &v) : QVector<QPoint>(v) {}
-#ifdef Q_COMPILER_RVALUE_REFS
- /*implicit*/ QPolygon(QVector<QPoint> &&v) Q_DECL_NOTHROW : QVector<QPoint>(std::move(v)) {}
-#endif
+ /*implicit*/ QPolygon(QVector<QPoint> &&v) noexcept : QVector<QPoint>(std::move(v)) {}
QPolygon(const QRect &r, bool closed=false);
QPolygon(int nPoints, const int *points);
QPolygon(const QPolygon &other) : QVector<QPoint>(other) {}
-#ifdef Q_COMPILER_RVALUE_REFS
- QPolygon(QPolygon &&other) Q_DECL_NOTHROW : QVector<QPoint>(std::move(other)) {}
- QPolygon &operator=(QPolygon &&other) Q_DECL_NOTHROW { swap(other); return *this; }
-#endif
+ QPolygon(QPolygon &&other) noexcept : QVector<QPoint>(std::move(other)) {}
+ QPolygon &operator=(QPolygon &&other) noexcept { swap(other); return *this; }
QPolygon &operator=(const QPolygon &other) { QVector<QPoint>::operator=(other); return *this; }
- void swap(QPolygon &other) Q_DECL_NOTHROW { QVector<QPoint>::swap(other); } // prevent QVector<QPoint><->QPolygon swaps
+ void swap(QPolygon &other) noexcept { QVector<QPoint>::swap(other); } // prevent QVector<QPoint><->QPolygon swaps
operator QVariant() const;
@@ -145,16 +141,12 @@ public:
inline ~QPolygonF() {}
inline explicit QPolygonF(int size);
inline /*implicit*/ QPolygonF(const QVector<QPointF> &v) : QVector<QPointF>(v) {}
-#ifdef Q_COMPILER_RVALUE_REFS
- /* implicit */ QPolygonF(QVector<QPointF> &&v) Q_DECL_NOTHROW : QVector<QPointF>(std::move(v)) {}
-#endif
+ /* implicit */ QPolygonF(QVector<QPointF> &&v) noexcept : QVector<QPointF>(std::move(v)) {}
QPolygonF(const QRectF &r);
/*implicit*/ QPolygonF(const QPolygon &a);
inline QPolygonF(const QPolygonF &a) : QVector<QPointF>(a) {}
-#ifdef Q_COMPILER_RVALUE_REFS
- QPolygonF(QPolygonF &&other) Q_DECL_NOTHROW : QVector<QPointF>(std::move(other)) {}
- QPolygonF &operator=(QPolygonF &&other) Q_DECL_NOTHROW { swap(other); return *this; }
-#endif
+ QPolygonF(QPolygonF &&other) noexcept : QVector<QPointF>(std::move(other)) {}
+ QPolygonF &operator=(QPolygonF &&other) noexcept { swap(other); return *this; }
QPolygonF &operator=(const QPolygonF &other) { QVector<QPointF>::operator=(other); return *this; }
inline void swap(QPolygonF &other) { QVector<QPointF>::swap(other); } // prevent QVector<QPointF><->QPolygonF swaps
diff --git a/src/gui/painting/qrbtree_p.h b/src/gui/painting/qrbtree_p.h
index d3ee23a91c..42e88889a1 100644
--- a/src/gui/painting/qrbtree_p.h
+++ b/src/gui/painting/qrbtree_p.h
@@ -60,7 +60,7 @@ struct QRBTree
{
struct Node
{
- inline Node() : parent(0), left(0), right(0), red(true) { }
+ inline Node() : parent(nullptr), left(nullptr), right(nullptr), red(true) { }
inline ~Node() {if (left) delete left; if (right) delete right;}
T data;
Node *parent;
@@ -69,7 +69,7 @@ struct QRBTree
bool red;
};
- inline QRBTree() : root(0), freeList(0) { }
+ inline QRBTree() : root(nullptr), freeList(nullptr) { }
inline ~QRBTree();
inline void clear();
@@ -120,7 +120,7 @@ inline QRBTree<T>::~QRBTree()
while (freeList) {
// Avoid recursively calling the destructor, as this list may become large.
Node *next = freeList->right;
- freeList->right = 0;
+ freeList->right = nullptr;
delete freeList;
freeList = next;
}
@@ -131,7 +131,7 @@ inline void QRBTree<T>::clear()
{
if (root)
delete root;
- root = 0;
+ root = nullptr;
}
template <class T>
@@ -359,7 +359,7 @@ void QRBTree<T>::detach(Node *node) // call this before removing a node.
ref = child;
if (child)
child->parent = node->parent;
- node->left = node->right = node->parent = 0;
+ node->left = node->right = node->parent = nullptr;
}
// 'node' must be black. rebalance will reduce the depth of black nodes by one in the sibling tree.
@@ -513,7 +513,7 @@ inline void QRBTree<T>::deleteNode(Node *&node)
detach(node);
node->right = freeList;
freeList = node;
- node = 0;
+ node = nullptr;
}
template <class T>
@@ -522,7 +522,7 @@ inline typename QRBTree<T>::Node *QRBTree<T>::newNode()
if (freeList) {
Node *node = freeList;
freeList = freeList->right;
- node->parent = node->left = node->right = 0;
+ node->parent = node->left = node->right = nullptr;
node->red = true;
return node;
}
diff --git a/src/gui/painting/qregion.cpp b/src/gui/painting/qregion.cpp
index 77718ce747..51eed962f0 100644
--- a/src/gui/painting/qregion.cpp
+++ b/src/gui/painting/qregion.cpp
@@ -1287,10 +1287,10 @@ struct QRegionPrivate {
}
}
- const QRect *begin() const Q_DECL_NOTHROW
+ const QRect *begin() const noexcept
{ return numRects == 1 ? &extents : rects.data(); } // avoid vectorize()
- const QRect *end() const Q_DECL_NOTHROW
+ const QRect *end() const noexcept
{ return begin() + numRects; }
inline void append(const QRect *r);
@@ -4321,7 +4321,7 @@ QRegion QRegion::xored(const QRegion &r) const
}
}
-QRect QRegion::boundingRect() const Q_DECL_NOTHROW
+QRect QRegion::boundingRect() const noexcept
{
if (isEmpty())
return QRect();
@@ -4379,12 +4379,12 @@ QVector<QRect> QRegion::rects() const
}
#endif
-QRegion::const_iterator QRegion::begin() const Q_DECL_NOTHROW
+QRegion::const_iterator QRegion::begin() const noexcept
{
return d->qt_rgn ? d->qt_rgn->begin() : nullptr;
}
-QRegion::const_iterator QRegion::end() const Q_DECL_NOTHROW
+QRegion::const_iterator QRegion::end() const noexcept
{
return d->qt_rgn ? d->qt_rgn->end() : nullptr;
}
@@ -4421,7 +4421,7 @@ void QRegion::setRects(const QRect *rects, int num)
}
}
-int QRegion::rectCount() const Q_DECL_NOTHROW
+int QRegion::rectCount() const noexcept
{
return (d->qt_rgn ? d->qt_rgn->numRects : 0);
}
diff --git a/src/gui/painting/qregion.h b/src/gui/painting/qregion.h
index 9fe6ed5675..54de916198 100644
--- a/src/gui/painting/qregion.h
+++ b/src/gui/painting/qregion.h
@@ -69,30 +69,28 @@ public:
QRegion(const QRect &r, RegionType t = Rectangle);
QRegion(const QPolygon &pa, Qt::FillRule fillRule = Qt::OddEvenFill);
QRegion(const QRegion &region);
- QRegion(QRegion &&other) Q_DECL_NOTHROW
+ QRegion(QRegion &&other) noexcept
: d(other.d) { other.d = const_cast<QRegionData*>(&shared_empty); }
QRegion(const QBitmap &bitmap);
~QRegion();
QRegion &operator=(const QRegion &);
-#ifdef Q_COMPILER_RVALUE_REFS
- inline QRegion &operator=(QRegion &&other) Q_DECL_NOEXCEPT
+ inline QRegion &operator=(QRegion &&other) noexcept
{ qSwap(d, other.d); return *this; }
-#endif
- inline void swap(QRegion &other) Q_DECL_NOEXCEPT { qSwap(d, other.d); }
+ inline void swap(QRegion &other) noexcept { qSwap(d, other.d); }
bool isEmpty() const;
bool isNull() const;
typedef const QRect *const_iterator;
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
- const_iterator begin() const Q_DECL_NOTHROW;
- const_iterator cbegin() const Q_DECL_NOTHROW { return begin(); }
- const_iterator end() const Q_DECL_NOTHROW;
- const_iterator cend() const Q_DECL_NOTHROW { return end(); }
- const_reverse_iterator rbegin() const Q_DECL_NOTHROW { return const_reverse_iterator(end()); }
- const_reverse_iterator crbegin() const Q_DECL_NOTHROW { return rbegin(); }
- const_reverse_iterator rend() const Q_DECL_NOTHROW { return const_reverse_iterator(begin()); }
- const_reverse_iterator crend() const Q_DECL_NOTHROW { return rend(); }
+ const_iterator begin() const noexcept;
+ const_iterator cbegin() const noexcept { return begin(); }
+ const_iterator end() const noexcept;
+ const_iterator cend() const noexcept { return end(); }
+ const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(end()); }
+ const_reverse_iterator crbegin() const noexcept { return rbegin(); }
+ const_reverse_iterator rend() const noexcept { return const_reverse_iterator(begin()); }
+ const_reverse_iterator crend() const noexcept { return rend(); }
bool contains(const QPoint &p) const;
bool contains(const QRect &r) const;
@@ -121,13 +119,13 @@ public:
bool intersects(const QRegion &r) const;
bool intersects(const QRect &r) const;
- QRect boundingRect() const Q_DECL_NOTHROW;
+ QRect boundingRect() const noexcept;
#if QT_DEPRECATED_SINCE(5, 11)
QT_DEPRECATED_X("Use begin()/end() instead")
QVector<QRect> rects() const;
#endif
void setRects(const QRect *rect, int num);
- int rectCount() const Q_DECL_NOTHROW;
+ int rectCount() const noexcept;
#ifdef Q_COMPILER_MANGLES_RETURN_TYPE
// ### Qt 6: remove these, they're kept for MSVC compat
const QRegion operator|(const QRegion &r) const;
diff --git a/src/gui/painting/qstroker.cpp b/src/gui/painting/qstroker.cpp
index 327190b678..5b6990e667 100644
--- a/src/gui/painting/qstroker.cpp
+++ b/src/gui/painting/qstroker.cpp
@@ -173,15 +173,12 @@ template <class Iterator> bool qt_stroke_side(Iterator *it, QStroker *stroker,
bool capFirst, QLineF *startTangent);
/*******************************************************************************
- * QLineF::angle gives us the smalles angle between two lines. Here we
- * want to identify the line's angle direction on the unit circle.
+ * QLineF::angleTo gives us the angle between two lines with respecting the direction.
+ * Here we want to identify the line's angle direction on the unit circle.
*/
static inline qreal adapted_angle_on_x(const QLineF &line)
{
- qreal angle = line.angle(QLineF(0, 0, 1, 0));
- if (line.dy() > 0)
- angle = 360 - angle;
- return angle;
+ return QLineF(0, 0, 1, 0).angleTo(line);
}
QStrokerOps::QStrokerOps()
@@ -459,7 +456,7 @@ void QStroker::joinPoints(qfixed focal_x, qfixed focal_y, const QLineF &nextLine
QLineF prevLine(qt_fixed_to_real(m_back2X), qt_fixed_to_real(m_back2Y),
qt_fixed_to_real(m_back1X), qt_fixed_to_real(m_back1Y));
QPointF isect;
- QLineF::IntersectType type = prevLine.intersect(nextLine, &isect);
+ QLineF::IntersectionType type = prevLine.intersects(nextLine, &isect);
if (join == FlatJoin) {
QLineF shortCut(prevLine.p2(), nextLine.p1());
diff --git a/src/gui/painting/qtextureglyphcache.cpp b/src/gui/painting/qtextureglyphcache.cpp
index 99b04aaba6..e8c47df21c 100644
--- a/src/gui/painting/qtextureglyphcache.cpp
+++ b/src/gui/painting/qtextureglyphcache.cpp
@@ -73,7 +73,7 @@ int QTextureGlyphCache::calculateSubPixelPositionCount(glyph_t glyph) const
if (path.isEmpty())
break;
- images[numImages++] = qMove(img);
+ images[numImages++] = std::move(img);
} else {
bool found = false;
for (int j = 0; j < numImages; ++j) {
@@ -83,7 +83,7 @@ int QTextureGlyphCache::calculateSubPixelPositionCount(glyph_t glyph) const
}
}
if (!found)
- images[numImages++] = qMove(img);
+ images[numImages++] = std::move(img);
}
}
@@ -285,6 +285,8 @@ QImageTextureGlyphCache::~QImageTextureGlyphCache()
void QImageTextureGlyphCache::resizeTextureData(int width, int height)
{
m_image = m_image.copy(0, 0, width, height);
+ // Regions not part of the copy are initialized to 0, and that is just what
+ // we need.
}
void QImageTextureGlyphCache::createTextureData(int width, int height)
@@ -305,6 +307,12 @@ void QImageTextureGlyphCache::createTextureData(int width, int height)
default:
Q_UNREACHABLE();
}
+
+ // Regions not touched by the glyphs must be initialized to 0. (such
+ // locations may in fact be sampled with styled (shifted) text materials)
+ // When resizing, the QImage copy() does this implicitly but the initial
+ // contents must be zeroed out explicitly here.
+ m_image.fill(0);
}
void QImageTextureGlyphCache::fillTexture(const Coord &c, glyph_t g, QFixed subPixelPosition)
diff --git a/src/gui/painting/qtextureglyphcache_p.h b/src/gui/painting/qtextureglyphcache_p.h
index 3da28872b1..1e83ab46d1 100644
--- a/src/gui/painting/qtextureglyphcache_p.h
+++ b/src/gui/painting/qtextureglyphcache_p.h
@@ -75,7 +75,7 @@ class Q_GUI_EXPORT QTextureGlyphCache : public QFontEngineGlyphCache
{
public:
QTextureGlyphCache(QFontEngine::GlyphFormat format, const QTransform &matrix)
- : QFontEngineGlyphCache(format, matrix), m_current_fontengine(0),
+ : QFontEngineGlyphCache(format, matrix), m_current_fontengine(nullptr),
m_w(0), m_h(0), m_cx(0), m_cy(0), m_currentRowHeight(0)
{ }
diff --git a/src/gui/painting/qtransform.cpp b/src/gui/painting/qtransform.cpp
index 816514a695..7696da7d45 100644
--- a/src/gui/painting/qtransform.cpp
+++ b/src/gui/painting/qtransform.cpp
@@ -265,7 +265,9 @@ QTransform::QTransform()
, m_13(0), m_23(0), m_33(1)
, m_type(TxNone)
, m_dirty(TxNone)
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
, d(nullptr)
+#endif
{
}
@@ -284,7 +286,9 @@ QTransform::QTransform(qreal h11, qreal h12, qreal h13,
, m_13(h13), m_23(h23), m_33(h33)
, m_type(TxNone)
, m_dirty(TxProject)
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
, d(nullptr)
+#endif
{
}
@@ -301,7 +305,9 @@ QTransform::QTransform(qreal h11, qreal h12, qreal h21,
, m_13(0), m_23(0), m_33(1)
, m_type(TxNone)
, m_dirty(TxShear)
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
, d(nullptr)
+#endif
{
}
@@ -317,7 +323,9 @@ QTransform::QTransform(const QMatrix &mtx)
m_13(0), m_23(0), m_33(1)
, m_type(TxNone)
, m_dirty(TxShear)
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
, d(nullptr)
+#endif
{
}
@@ -795,7 +803,7 @@ bool QTransform::operator==(const QTransform &o) const
Returns the hash value for \a key, using
\a seed to seed the calculation.
*/
-uint qHash(const QTransform &key, uint seed) Q_DECL_NOTHROW
+uint qHash(const QTransform &key, uint seed) noexcept
{
QtPrivate::QHashCombine hash;
seed = hash(seed, key.m11());
@@ -1021,7 +1029,7 @@ QTransform QTransform::operator*(const QTransform &m) const
/*!
Assigns the given \a matrix's values to this matrix.
*/
-QTransform & QTransform::operator=(const QTransform &matrix) Q_DECL_NOTHROW
+QTransform & QTransform::operator=(const QTransform &matrix) noexcept
{
affine._m11 = matrix.affine._m11;
affine._m12 = matrix.affine._m12;
diff --git a/src/gui/painting/qtransform.h b/src/gui/painting/qtransform.h
index 1e322d435a..b220770144 100644
--- a/src/gui/painting/qtransform.h
+++ b/src/gui/painting/qtransform.h
@@ -77,13 +77,13 @@ public:
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
// ### Qt 6: remove; the compiler-generated ones are fine!
- QTransform &operator=(QTransform &&other) Q_DECL_NOTHROW // = default
+ QTransform &operator=(QTransform &&other) noexcept // = default
{ memcpy(static_cast<void *>(this), static_cast<void *>(&other), sizeof(QTransform)); return *this; }
- QTransform &operator=(const QTransform &) Q_DECL_NOTHROW; // = default
- QTransform(QTransform &&other) Q_DECL_NOTHROW // = default
+ QTransform &operator=(const QTransform &) noexcept; // = default
+ QTransform(QTransform &&other) noexcept // = default
: affine(Qt::Uninitialized)
{ memcpy(static_cast<void *>(this), static_cast<void *>(&other), sizeof(QTransform)); }
- QTransform(const QTransform &other) Q_DECL_NOTHROW // = default
+ QTransform(const QTransform &other) noexcept // = default
: affine(Qt::Uninitialized)
{ memcpy(static_cast<void *>(this), static_cast<const void *>(&other), sizeof(QTransform)); }
#endif
@@ -176,7 +176,9 @@ private:
, m_13(h13), m_23(h23), m_33(h33)
, m_type(TxNone)
, m_dirty(TxProject)
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
, d(nullptr)
+#endif
{
}
inline QTransform(bool)
@@ -184,7 +186,9 @@ private:
, m_13(0), m_23(0), m_33(1)
, m_type(TxNone)
, m_dirty(TxNone)
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
, d(nullptr)
+#endif
{
}
inline TransformationType inline_type() const;
@@ -202,7 +206,7 @@ private:
};
Q_DECLARE_TYPEINFO(QTransform, Q_MOVABLE_TYPE);
-Q_GUI_EXPORT Q_DECL_CONST_FUNCTION uint qHash(const QTransform &key, uint seed = 0) Q_DECL_NOTHROW;
+Q_GUI_EXPORT Q_DECL_CONST_FUNCTION uint qHash(const QTransform &key, uint seed = 0) noexcept;
/******* inlines *****/
inline QTransform::TransformationType QTransform::inline_type() const
diff --git a/src/gui/painting/qtriangulator_p.h b/src/gui/painting/qtriangulator_p.h
index c9ae2571f4..177e5e66ed 100644
--- a/src/gui/painting/qtriangulator_p.h
+++ b/src/gui/painting/qtriangulator_p.h
@@ -57,7 +57,7 @@
QT_BEGIN_NAMESPACE
-class Q_GUI_EXPORT QVertexIndexVector
+class QVertexIndexVector
{
public:
enum Type {
@@ -93,19 +93,6 @@ public:
return indices16.size();
}
- QVertexIndexVector() = default;
- QVertexIndexVector(const QVertexIndexVector &other) = default;
- inline QVertexIndexVector &operator = (const QVertexIndexVector &other)
- {
- if (t == UnsignedInt)
- indices32 = other.indices32;
- else
- indices16 = other.indices16;
-
- t = other.t;
- return *this;
- }
-
private:
Type t;
@@ -113,23 +100,15 @@ private:
QVector<quint16> indices16;
};
-struct Q_GUI_EXPORT QTriangleSet
+struct QTriangleSet
{
- inline QTriangleSet() { }
- inline QTriangleSet(const QTriangleSet &other) : vertices(other.vertices), indices(other.indices) { }
- QTriangleSet &operator = (const QTriangleSet &other) {vertices = other.vertices; indices = other.indices; return *this;}
-
// The vertices of a triangle are given by: (x[i[n]], y[i[n]]), (x[j[n]], y[j[n]]), (x[k[n]], y[k[n]]), n = 0, 1, ...
QVector<qreal> vertices; // [x[0], y[0], x[1], y[1], x[2], ...]
QVertexIndexVector indices; // [i[0], j[0], k[0], i[1], j[1], k[1], i[2], ...]
};
-struct Q_GUI_EXPORT QPolylineSet
+struct QPolylineSet
{
- inline QPolylineSet() { }
- inline QPolylineSet(const QPolylineSet &other) : vertices(other.vertices), indices(other.indices) { }
- QPolylineSet &operator = (const QPolylineSet &other) {vertices = other.vertices; indices = other.indices; return *this;}
-
QVector<qreal> vertices; // [x[0], y[0], x[1], y[1], x[2], ...]
QVertexIndexVector indices; // End of polyline is marked with -1.
};
diff --git a/src/gui/painting/qvectorpath_p.h b/src/gui/painting/qvectorpath_p.h
index 1b649a5d2a..df5772d4cc 100644
--- a/src/gui/painting/qvectorpath_p.h
+++ b/src/gui/painting/qvectorpath_p.h
@@ -106,7 +106,7 @@ public:
// ### Falcon: introduca a struct XY for points so lars is not so confused...
QVectorPath(const qreal *points,
int count,
- const QPainterPath::ElementType *elements = 0,
+ const QPainterPath::ElementType *elements = nullptr,
uint hints = ArbitraryShapeHint)
: m_elements(elements),
m_points(points),
@@ -128,12 +128,12 @@ public:
inline bool hasExplicitOpen() const { return m_hints & ExplicitOpen; }
inline bool hasWindingFill() const { return m_hints & WindingFill; }
- inline void makeCacheable() const { m_hints |= ShouldUseCacheHint; m_cache = 0; }
+ inline void makeCacheable() const { m_hints |= ShouldUseCacheHint; m_cache = nullptr; }
inline uint hints() const { return m_hints; }
inline const QPainterPath::ElementType *elements() const { return m_elements; }
inline const qreal *points() const { return m_points; }
- inline bool isEmpty() const { return m_points == 0; }
+ inline bool isEmpty() const { return m_points == nullptr; }
inline int elementCount() const { return m_count; }
inline const QPainterPath convertToPainterPath() const;
@@ -165,7 +165,7 @@ public:
return e;
e = e->next;
}
- return 0;
+ return nullptr;
}
template <typename T> static inline bool isRect(const T *pts, int elementCount) {
diff --git a/src/gui/rhi/qrhi.cpp b/src/gui/rhi/qrhi.cpp
new file mode 100644
index 0000000000..8cd4813723
--- /dev/null
+++ b/src/gui/rhi/qrhi.cpp
@@ -0,0 +1,4891 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gui module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qrhi_p_p.h"
+#include <qmath.h>
+
+#include "qrhinull_p_p.h"
+#ifndef QT_NO_OPENGL
+#include "qrhigles2_p_p.h"
+#endif
+#if QT_CONFIG(vulkan)
+#include "qrhivulkan_p_p.h"
+#endif
+#ifdef Q_OS_WIN
+#include "qrhid3d11_p_p.h"
+#endif
+//#ifdef Q_OS_DARWIN
+#ifdef Q_OS_MACOS
+#include "qrhimetal_p_p.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QRhi
+ \inmodule QtRhi
+
+ \brief Accelerated 2D/3D graphics API abstraction.
+
+ The Qt Rendering Hardware Interface is an abstraction for hardware accelerated
+ graphics APIs, such as, \l{https://www.khronos.org/opengl/}{OpenGL},
+ \l{https://www.khronos.org/opengles/}{OpenGL ES},
+ \l{https://docs.microsoft.com/en-us/windows/desktop/direct3d}{Direct3D},
+ \l{https://developer.apple.com/metal/}{Metal}, and
+ \l{https://www.khronos.org/vulkan/}{Vulkan}.
+
+ Some of the main design goals are:
+
+ \list
+
+ \li Simple, minimal, understandable, extensible. Follow the proven path of the
+ Qt Quick scenegraph.
+
+ \li Aim to be a product - and in the bigger picture, part of a product (Qt) -
+ that is usable out of the box both by internal (such as, Qt Quick) and,
+ eventually, external users.
+
+ \li Not a complete 1:1 wrapper for any of the underlying APIs. The feature set
+ is tuned towards the needs of Qt's 2D and 3D offering (QPainter, Qt Quick, Qt
+ 3D Studio). Iterate and evolve in a sustainable manner.
+
+ \li Intrinsically cross-platform, without reinventing: abstracting
+ cross-platform aspects of certain APIs (such as, OpenGL context creation and
+ windowing system interfaces, Vulkan instance and surface management) is not in
+ scope here. These are delegated to the existing QtGui facilities (QWindow,
+ QOpenGLContext, QVulkanInstance) and its backing QPA architecture.
+
+ \endlist
+
+ Each QRhi instance is backed by a backend for a specific graphics API. The
+ selection of the backend is a run time choice and is up to the application
+ or library that creates the QRhi instance. Some backends are available on
+ multiple platforms (OpenGL, Vulkan, Null), while APIs specific to a given
+ platform are only available when running on the platform in question (Metal
+ on macOS/iOS/tvOS, Direct3D on Windows).
+
+ The available backends currently are:
+
+ \list
+
+ \li OpenGL 2.1 or OpenGL ES 2.0 or newer. Some extensions are utilized when
+ present, for example to enable multisample framebuffers.
+
+ \li Direct3D 11.1
+
+ \li Metal
+
+ \li Vulkan 1.0, optionally with some extensions that are part of Vulkan 1.1
+
+ \li Null - A "dummy" backend that issues no graphics calls at all.
+
+ \endlist
+
+ In order to allow shader code to be written once in Qt applications and
+ libraries, all shaders are expected to be written in a single language
+ which is then compiled into SPIR-V. Versions for various shading language
+ are then generated from that, together with reflection information (inputs,
+ outputs, shader resources). This is then packed into easily and efficiently
+ serializable QShader instances. The compilers and tools to generate such
+ shaders are not part of QRhi, but the core classes for using such shaders,
+ QShader and QShaderDescription, are.
+
+ \section2 Design Fundamentals
+
+ A QRhi cannot be instantiated directly. Instead, use the create()
+ function. Delete the QRhi instance normally to release the graphics device.
+
+ \section3 Resources
+
+ Instances of classes deriving from QRhiResource, such as, QRhiBuffer,
+ QRhiTexture, etc., encapsulate zero, one, or more native graphics
+ resources. Instances of such classes are always created via the \c new
+ functions of the QRhi, such as, newBuffer(), newTexture(),
+ newTextureRenderTarget(), newSwapChain().
+
+ \badcode
+ vbuf = rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(vertexData));
+ if (!vbuf->build()) { error }
+ ...
+ delete vbuf;
+ \endcode
+
+ \list
+
+ \li The returned value from both create() and functions like newBuffer() is
+ owned by the caller.
+
+ \li Just creating a QRhiResource subclass never allocates or initializes any
+ native resources. That is only done when calling the \c build function of a
+ subclass, for example, QRhiBuffer::build() or QRhiTexture::build().
+
+ \li The exception is
+ QRhiTextureRenderTarget::newCompatibleRenderPassDescriptor() and
+ QRhiSwapChain::newCompatibleRenderPassDescriptor(). There is no \c build
+ operation for these and the returned object is immediately active.
+
+ \li The resource objects themselves are treated as immutable: once a
+ resource is built, changing any parameters via the setters, such as,
+ QRhiTexture::setPixelSize(), has no effect, unless the underlying native
+ resource is released and \c build is called again. See more about resource
+ reuse in the sections below.
+
+ \li The underlying native resources are scheduled for releasing by the
+ QRhiResource destructor, or by calling QRhiResource::release(). Backends
+ often queue release requests and defer executing them to an unspecified
+ time, this is hidden from the applications. This way applications do not
+ have to worry about releasing native resources that may still be in use by
+ an in-flight frame.
+
+ \li Note that this does not mean that a QRhiResource can freely be
+ destroyed or release()'d within a frame (that is, in a
+ \l{QRhiCommandBuffer::beginFrame()}{beginFrame()} -
+ \l{QRhiCommandBuffer::endFrame()}{endFrame()} section). As a general rule,
+ all referenced QRhiResource objects must stay unchanged until the frame is
+ submitted by calling \l{QRhiCommandBuffer::endFrame()}{endFrame()}. To ease
+ this, QRhiResource::releaseAndDestroyLater() is provided as a convenience.
+
+ \endlist
+
+ \section3 Command buffers and deferred command execution
+
+ Regardless of the design and capabilities of the underlying graphics API,
+ all QRhi backends implement some level of command buffers. No
+ QRhiCommandBuffer function issues any native bind or draw command (such as,
+ \c glDrawElements) directly. Commands are always recorded in a queue,
+ either native or provided by the QRhi backend. The command buffer is
+ submitted, and so execution starts only upon QRhi::endFrame() or
+ QRhi::finish().
+
+ The deferred nature has consequences for some types of objects. For example,
+ writing to a dynamic buffer multiple times within a frame, in case such
+ buffers are backed by host-visible memory, will result in making the
+ results of all writes are visible to all draw calls in the command buffer
+ of the frame, regardless of when the dynamic buffer update was recorded
+ relative to a draw call.
+
+ Furthermore, instances of QRhiResource subclasses must be treated immutable
+ within a frame in which they are referenced in any way. Create or rebuild
+ all resources upfront, before starting to record commands for the next
+ frame. Reusing a QRhiResource instance within a frame (by rebuilding it and
+ then referencing it again in the same \c{beginFrame - endFrame} section)
+ should be avoided as it may lead to unexpected results, depending on the
+ backend.
+
+ As a general rule, all referenced QRhiResource objects must stay valid and
+ unmodified until the frame is submitted by calling
+ \l{QRhiCommandBuffer::endFrame()}{endFrame()}. On the other hand, calling
+ \l{QRhiResource::release()}{release()} or destroying the QRhiResource are
+ always safe once the frame is submitted, regardless of the status of the
+ underlying native resources (which may still be in use by the GPU - but
+ that is taken care of internally).
+
+ Unlike APIs like OpenGL, upload and copy type of commands cannot be mixed
+ with draw commands. The typical renderer will involve a sequence similar to
+ the following: \c{(re)build resources} - \c{begin frame} - \c{record
+ uploads and copies} - \c{start renderpass} - \c{record draw calls} - \c{end
+ renderpass} - \c{end frame}. Recording copy type of operations happens via
+ QRhiResourceUpdateBatch. Such operations are committed typically on
+ \l{QRhiCommandBuffer::beginPass()}{beginPass()}.
+
+ When working with legacy rendering engines designed for OpenGL, the
+ migration to QRhi often involves redesigning from having a single \c render
+ step (that performs copies and uploads, clears buffers, and issues draw
+ calls, all mixed together) to a clearly separated, two phase \c prepare -
+ \c render setup where the \c render step only starts a renderpass and
+ records draw calls, while all resource creation and queuing of updates,
+ uploads and copies happens beforehand, in the \c prepare step.
+
+ QRhi does not at the moment allow freely creating and submitting command
+ buffers. This may be lifted in the future to some extent, in particular if
+ compute support is introduced, but the model of well defined
+ \c{frame-start} and \c{frame-end} points, combined with a dedicated,
+ "frame" command buffer, where \c{frame-end} implies presenting, is going to
+ remain the primary way of operating since this is what fits Qt's various UI
+ technologies best.
+
+ \section3 Threading
+
+ A QRhi instance and the associated resources can be created and used on any
+ thread but all usage must be limited to that one single thread. When
+ rendering to multiple QWindows in an application, having a dedicated thread
+ and QRhi instance for each window is often advisable, as this can eliminate
+ issues with unexpected throttling caused by presenting to multiple windows.
+ Conceptually that is then the same as how Qt Quick scene graph's threaded
+ render loop operates when working directly with OpenGL: one thread for each
+ window, one QOpenGLContext for each thread. When moving onto QRhi,
+ QOpenGLContext is replaced by QRhi, making the migration straightforward.
+
+ When it comes to externally created native objects, such as OpenGL contexts
+ passed in via QRhiGles2NativeHandles, it is up to the application to ensure
+ they are not misused by other threads.
+
+ Resources are not shareable between QRhi instances. This is an intentional
+ choice since QRhi hides most queue, command buffer, and resource
+ synchronization related tasks, and provides no API for them. Safe and
+ efficient concurrent use of graphics resources from multiple threads is
+ tied to those concepts, however, and is thus a topic that is currently out
+ of scope, but may be introduced in the future.
+
+ \section3 Resource synchronization
+
+ QRhi does not expose APIs for resource barriers or image layout
+ transitions. Such synchronization is done implicitly by the backends, where
+ applicable (for example, Vulkan), by tracking resource usage as necessary.
+
+ \section3 Resource reuse
+
+ From the user's point of view a QRhiResource is reusable immediately after
+ calling QRhiResource::release(). With the exception of swapchains, calling
+ \c build() on an already built object does an implicit \c release(). This
+ provides a handy shortcut to reuse a QRhiResource instance with different
+ parameters, with a new native graphics object underneath.
+
+ The importance of reusing the same object lies in the fact that some
+ objects reference other objects: for example, a QRhiShaderResourceBindings
+ can reference QRhiBuffer, QRhiTexture, and QRhiSampler instances. If in a
+ later frame one of these buffers need to be resized or a sampler parameter
+ needs changing, destroying and creating a whole new QRhiBuffer or
+ QRhiSampler would invalidate all references to the old instance. By just
+ changing the appropriate parameters via QRhiBuffer::setSize() or similar
+ and then calling QRhiBuffer::build(), everything works as expected and
+ there is no need to touch the QRhiShaderResourceBindings at all, even
+ though there is a good chance that under the hood the QRhiBuffer is now
+ backed by a whole new native buffer.
+
+ \badcode
+ ubuf = rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 256);
+ ubuf->build();
+
+ srb = rhi->newShaderResourceBindings()
+ srb->setBindings({
+ QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, ubuf)
+ });
+ srb->build();
+
+ ...
+
+ // now in a later frame we need to grow the buffer to a larger size
+ ubuf->setSize(512);
+ ubuf->build(); // same as ubuf->release(); ubuf->build();
+
+ // that's it, srb needs no changes whatsoever
+ \endcode
+
+ \section3 Pooled objects
+
+ In addition to resources, there are pooled objects as well, such as,
+ QRhiResourceUpdateBatch. An instance is retrieved via a \c next function,
+ such as, nextResourceUpdateBatch(). The caller does not own the returned
+ instance in this case. The only valid way of operating here is calling
+ functions on the QRhiResourceUpdateBatch and then passing it to
+ QRhiCommandBuffer::beginPass() or QRhiCommandBuffer::endPass(). These
+ functions take care of returning the batch to the pool. Alternatively, a
+ batch can be "canceled" and returned to the pool without processing by
+ calling QRhiResourceUpdateBatch::release().
+
+ A typical pattern is thus:
+
+ \badcode
+ QRhiResourceUpdateBatch *resUpdates = rhi->nextResourceUpdateBatch();
+ ...
+ resUpdates->updateDynamicBuffer(ubuf, 0, 64, mvp.constData());
+ if (!image.isNull()) {
+ resUpdates->uploadTexture(texture, image);
+ image = QImage();
+ }
+ ...
+ QRhiCommandBuffer *cb = m_sc->currentFrameCommandBuffer();
+ cb->beginPass(swapchain->currentFrameRenderTarget(), clearCol, clearDs, resUpdates);
+ \endcode
+
+ \section3 Swapchain specifics
+
+ QRhiSwapChain features some special semantics due to the peculiar nature of
+ swapchains.
+
+ \list
+
+ \li It has no \c build but rather a QRhiSwapChain::buildOrResize().
+ Repeatedly calling this function is \b not the same as calling
+ QRhiSwapChain::release() followed by QRhiSwapChain::buildOrResize(). This
+ is because swapchains often have ways to handle the case where buffers need
+ to be resized in a manner that is more efficient than a brute force
+ destroying and recreating from scratch.
+
+ \li An active QRhiSwapChain must be released by calling
+ \l{QRhiSwapChain::release()}{release()}, or by destroying the object, before
+ the QWindow's underlying QPlatformWindow, and so the associated native
+ window object, is destroyed. It should not be postponed because releasing
+ the swapchain may become problematic (and with some APIs, like Vulkan, is
+ explicitly disallowed) when the native window is not around anymore, for
+ example because the QPlatformWindow got destroyed upon getting a
+ QWindow::close(). Therefore, releasing the swapchain must happen whenever
+ the targeted QWindow sends the
+ QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed event. If the event does
+ not arrive before the destruction of the QWindow - this can happen when
+ using QCoreApplication::quit() -, then check QWindow::handle() after the
+ event loop exits and invoke the swapchain release when non-null (meaning
+ the underlying native window is still around).
+
+ \endlist
+
+ \section3 Ownership
+
+ The general rule is no ownership transfer. Creating a QRhi with an already
+ existing graphics device does not mean the QRhi takes ownership of the
+ device object. Similarly, ownership is not given away when a device or
+ texture object is "exported" via QRhi::nativeHandles() or
+ QRhiTexture::nativeHandles(). Most importantly, passing pointers in structs
+ and via setters does not transfer ownership.
+ */
+
+/*!
+ \enum QRhi::Implementation
+ Describes which graphics API-specific backend gets used by a QRhi instance.
+
+ \value Null
+ \value Vulkan
+ \value OpenGLES2
+ \value D3D11
+ \value Metal
+ */
+
+/*!
+ \enum QRhi::Flag
+ Describes what special features to enable.
+
+ \value EnableProfiling Enables gathering timing (CPU, GPU) and resource
+ (QRhiBuffer, QRhiTexture, etc.) information and additional metadata. See
+ QRhiProfiler. Avoid enabling in production builds as it may involve a
+ performance penalty.
+
+ \value EnableDebugMarkers Enables debug marker groups. Without this frame
+ debugging features like making debug groups and custom resource name
+ visible in external GPU debugging tools will not be available and functions
+ like QRhiCommandBuffer::debugMarkBegin() will become a no-op. Avoid
+ enabling in production builds as it may involve a performance penalty.
+ */
+
+/*!
+ \enum QRhi::FrameOpResult
+ Describes the result of operations that can have a soft failure.
+
+ \value FrameOpSuccess Success
+
+ \value FrameOpError Unspecified error
+
+ \value FrameOpSwapChainOutOfDate The swapchain is in an inconsistent state
+ internally. This can be recoverable by attempting to repeat the operation
+ (such as, beginFrame()) later.
+
+ \value FrameOpDeviceLost The graphics device was lost. This can be
+ recoverable by attempting to repeat the operation (such as, beginFrame())
+ and releasing and reinitializing all objects backed by native graphics
+ resources.
+ */
+
+/*!
+ \enum QRhi::Feature
+ Flag values to indicate what features are supported by the backend currently in use.
+
+ \value MultisampleTexture Indicates that textures with a sample count larger
+ than 1 are supported.
+
+ \value MultisampleRenderBuffer Indicates that renderbuffers with a sample
+ count larger than 1 are supported.
+
+ \value DebugMarkers Indicates that debug marker groups (and so
+ QRhiCommandBuffer::debugMarkBegin()) are supported.
+
+ \value Timestamps Indicates that command buffer timestamps are supported.
+ Relevant for QRhiProfiler::gpuFrameTimes().
+
+ \value Instancing Indicates that instanced drawing is supported.
+
+ \value CustomInstanceStepRate Indicates that instance step rates other than
+ 1 are supported.
+
+ \value PrimitiveRestart Indicates that restarting the assembly of
+ primitives when encountering an index value of 0xFFFF
+ (\l{QRhiCommandBuffer::IndexUInt16}{IndexUInt16}) or 0xFFFFFFFF
+ (\l{QRhiCommandBuffer::IndexUInt32}{IndexUInt32}) is enabled, for certain
+ primitive topologies at least. QRhi will try to enable this with all
+ backends, but in some cases it will not be supported. Dynamically
+ controlling primitive restart is not possible since with some APIs
+ primitive restart with a fixed index is always on. Applications must assume
+ that whenever this feature is reported as supported, the above mentioned
+ index values \c may be treated specially, depending on the topology. The
+ only two topologies where primitive restart is guaranteed to behave
+ identically across backends, as long as this feature is reported as
+ supported, are \l{QRhiGraphicsPipeline::LineStrip}{LineStrip} and
+ \l{QRhiGraphicsPipeline::TriangleStrip}{TriangleStrip}.
+
+ \value NonDynamicUniformBuffers Indicates that creating buffers with the
+ usage \l{QRhiBuffer::UniformBuffer}{UniformBuffer} and the types
+ \l{QRhiBuffer::Immutable}{Immutable} or \l{QRhiBuffer::Static}{Static} is
+ supported. When reported as unsupported, uniform (constant) buffers must be
+ created as \l{QRhiBuffer::Dynamic}{Dynamic}. (which is recommended
+ regardless)
+
+ \value NonFourAlignedEffectiveIndexBufferOffset Indicates that effective
+ index buffer offsets (\c{indexOffset + firstIndex * indexComponentSize})
+ that are not 4 byte aligned are supported. When not supported, attempting
+ to issue a \l{QRhiCommandBuffer::drawIndexed()}{drawIndexed()} with a
+ non-aligned effective offset may lead to unspecified behavior.
+
+ \value NPOTTextureRepeat Indicates that the \l{QRhiSampler::Repeat}{Repeat}
+ mode is supported for textures with a non-power-of-two size.
+
+ \value RedOrAlpha8IsRed Indicates that the
+ \l{QRhiTexture::RED_OR_ALPHA8}{RED_OR_ALPHA8} format maps to a one
+ component 8-bit \c red format. This is the case for all backends except
+ OpenGL, where \c{GL_ALPHA}, a one component 8-bit \c alpha format, is used
+ instead. This is relevant for shader code that samples from the texture.
+
+ \value ElementIndexUint Indicates that 32-bit unsigned integer elements are
+ supported in the index buffer. In practice this is true everywhere except
+ when running on plain OpenGL ES 2.0 implementations without the necessary
+ extension. When false, only 16-bit unsigned elements are supported in the
+ index buffer.
+ */
+
+/*!
+ \enum QRhi::BeginFrameFlag
+ Flag values for QRhi::beginFrame()
+ */
+
+/*!
+ \enum QRhi::EndFrameFlag
+ Flag values for QRhi::endFrame()
+
+ \value SkipPresent Specifies that no present command is to be queued or no
+ swapBuffers call is to be made. This way no image is presented. Generating
+ multiple frames with all having this flag set is not recommended (except,
+ for example, for benchmarking purposes - but keep in mind that backends may
+ behave differently when it comes to waiting for command completion without
+ presenting so the results are not comparable between them)
+ */
+
+/*!
+ \enum QRhi::ResourceLimit
+ Describes the resource limit to query.
+
+ \value TextureSizeMin Minimum texture width and height. This is typically
+ 1. The minimum texture size is handled gracefully, meaning attempting to
+ create a texture with an empty size will instead create a texture with the
+ minimum size.
+
+ \value TextureSizeMax Maximum texture width and height. This depends on the
+ graphics API and sometimes the platform or implementation as well.
+ Typically the value is in the range 4096 - 16384. Attempting to create
+ textures larger than this is expected to fail.
+
+ \value MaxColorAttachments The maximum number of color attachments for a
+ QRhiTextureRenderTarget, in case multiple render targets are supported. When
+ MRT is not supported, the value is 1. Otherwise this is typically 8, but
+ watch out for the fact that OpenGL only mandates 4 as the minimum, and that
+ is what some OpenGL ES implementations provide.
+
+ \value FramesInFlight The number of frames the backend may keep "in
+ flight". The value has no relevance, and is unspecified, with backends like
+ OpenGL and Direct3D 11. With backends like Vulkan or Metal, it is the
+ responsibility of QRhi to block whenever starting a new frame and finding
+ the CPU is already \c{N - 1} frames ahead of the GPU (because the command
+ buffer submitted in frame no. \c{current} - \c{N} has not yet completed).
+ The value N is what is returned from here, and is typically 2. This can be
+ relevant to applications that integrate rendering done directly with the
+ graphics API, as such rendering code may want to perform double (if the
+ value is 2) buffering for resources, such as, buffers, similarly to the
+ QRhi backends themselves. The current frame slot index (a value running 0,
+ 1, .., N-1, then wrapping around) is retrievable from
+ QRhi::currentFrameSlot().
+ */
+
+/*!
+ \class QRhiInitParams
+ \inmodule QtRhi
+ \brief Base class for backend-specific initialization parameters.
+
+ Contains fields that are relevant to all backends.
+ */
+
+/*!
+ \class QRhiDepthStencilClearValue
+ \inmodule QtRhi
+ \brief Specifies clear values for a depth or stencil buffer.
+ */
+
+/*!
+ \fn QRhiDepthStencilClearValue::QRhiDepthStencilClearValue()
+
+ Constructs a depth/stencil clear value with depth clear value 1.0f and
+ stencil clear value 0.
+ */
+
+/*!
+ Constructs a depth/stencil clear value with depth clear value \a d and
+ stencil clear value \a s.
+ */
+QRhiDepthStencilClearValue::QRhiDepthStencilClearValue(float d, quint32 s)
+ : m_d(d),
+ m_s(s)
+{
+}
+
+/*!
+ \return \c true if the values in the two QRhiDepthStencilClearValue objects
+ \a a and \a b are equal.
+
+ \relates QRhiDepthStencilClearValue
+ */
+bool operator==(const QRhiDepthStencilClearValue &a, const QRhiDepthStencilClearValue &b) Q_DECL_NOTHROW
+{
+ return a.depthClearValue() == b.depthClearValue()
+ && a.stencilClearValue() == b.stencilClearValue();
+}
+
+/*!
+ \return \c false if the values in the two QRhiDepthStencilClearValue
+ objects \a a and \a b are equal; otherwise returns \c true.
+
+ \relates QRhiDepthStencilClearValue
+*/
+bool operator!=(const QRhiDepthStencilClearValue &a, const QRhiDepthStencilClearValue &b) Q_DECL_NOTHROW
+{
+ return !(a == b);
+}
+
+/*!
+ \return the hash value for \a v, using \a seed to seed the calculation.
+
+ \relates QRhiDepthStencilClearValue
+ */
+uint qHash(const QRhiDepthStencilClearValue &v, uint seed) Q_DECL_NOTHROW
+{
+ return seed * (qFloor(v.depthClearValue() * 100) + v.stencilClearValue());
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug dbg, const QRhiDepthStencilClearValue &v)
+{
+ QDebugStateSaver saver(dbg);
+ dbg.nospace() << "QRhiDepthStencilClearValue(depth-clear=" << v.depthClearValue()
+ << " stencil-clear=" << v.stencilClearValue()
+ << ')';
+ return dbg;
+}
+#endif
+
+/*!
+ \class QRhiViewport
+ \inmodule QtRhi
+ \brief Specifies a viewport rectangle.
+
+ Used with QRhiCommandBuffer::setViewport().
+
+ \note QRhi assumes OpenGL-style viewport coordinates, meaning x and y are
+ bottom-left.
+
+ Typical usage is like the following:
+
+ \badcode
+ const QSize outputSizeInPixels = swapchain->currentPixelSize();
+ const QRhiViewport viewport(0, 0, outputSizeInPixels.width(), outputSizeInPixels.height());
+ cb->beginPass(swapchain->currentFrameRenderTarget(), { 0, 0, 0, 1 }, { 1, 0 });
+ cb->setGraphicsPipeline(ps);
+ cb->setViewport(viewport);
+ ...
+ \endcode
+
+ \sa QRhiCommandBuffer::setViewport(), QRhi::clipSpaceCorrMatrix(), QRhiScissor
+ */
+
+/*!
+ \fn QRhiViewport::QRhiViewport()
+
+ Constructs a viewport description with an empty rectangle and a depth range
+ of 0.0f - 1.0f.
+
+ \sa QRhi::clipSpaceCorrMatrix()
+ */
+
+/*!
+ Constructs a viewport description with the rectangle specified by \a x, \a
+ y, \a w, \a h and the depth range \a minDepth and \a maxDepth.
+
+ \note x and y are assumed to be the bottom-left position.
+
+ \sa QRhi::clipSpaceCorrMatrix()
+ */
+QRhiViewport::QRhiViewport(float x, float y, float w, float h, float minDepth, float maxDepth)
+ : m_rect { { x, y, w, h } },
+ m_minDepth(minDepth),
+ m_maxDepth(maxDepth)
+{
+}
+
+/*!
+ \return \c true if the values in the two QRhiViewport objects
+ \a a and \a b are equal.
+
+ \relates QRhiViewport
+ */
+bool operator==(const QRhiViewport &a, const QRhiViewport &b) Q_DECL_NOTHROW
+{
+ return a.viewport() == b.viewport()
+ && a.minDepth() == b.minDepth()
+ && a.maxDepth() == b.maxDepth();
+}
+
+/*!
+ \return \c false if the values in the two QRhiViewport
+ objects \a a and \a b are equal; otherwise returns \c true.
+
+ \relates QRhiViewport
+*/
+bool operator!=(const QRhiViewport &a, const QRhiViewport &b) Q_DECL_NOTHROW
+{
+ return !(a == b);
+}
+
+/*!
+ \return the hash value for \a v, using \a seed to seed the calculation.
+
+ \relates QRhiViewport
+ */
+uint qHash(const QRhiViewport &v, uint seed) Q_DECL_NOTHROW
+{
+ const std::array<float, 4> r = v.viewport();
+ return seed + r[0] + r[1] + r[2] + r[3] + qFloor(v.minDepth() * 100) + qFloor(v.maxDepth() * 100);
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug dbg, const QRhiViewport &v)
+{
+ QDebugStateSaver saver(dbg);
+ const std::array<float, 4> r = v.viewport();
+ dbg.nospace() << "QRhiViewport(bottom-left-x=" << r[0]
+ << " bottom-left-y=" << r[1]
+ << " width=" << r[2]
+ << " height=" << r[3]
+ << " minDepth=" << v.minDepth()
+ << " maxDepth=" << v.maxDepth()
+ << ')';
+ return dbg;
+}
+#endif
+
+/*!
+ \class QRhiScissor
+ \inmodule QtRhi
+ \brief Specifies a scissor rectangle.
+
+ Used with QRhiCommandBuffer::setScissor(). Setting a scissor rectangle is
+ only possible with a QRhiGraphicsPipeline that has
+ QRhiGraphicsPipeline::UsesScissor set.
+
+ \note QRhi assumes OpenGL-style scissor coordinates, meaning x and y are
+ bottom-left.
+
+ \sa QRhiCommandBuffer::setScissor(), QRhiViewport
+ */
+
+/*!
+ \fn QRhiScissor::QRhiScissor()
+
+ Constructs an empty scissor.
+ */
+
+/*!
+ Constructs a scissor with the rectangle specified by \a x, \a y, \a w, and
+ \a h.
+
+ \note x and y are assumed to be the bottom-left position.
+ */
+QRhiScissor::QRhiScissor(int x, int y, int w, int h)
+ : m_rect { { x, y, w, h } }
+{
+}
+
+/*!
+ \return \c true if the values in the two QRhiScissor objects
+ \a a and \a b are equal.
+
+ \relates QRhiScissor
+ */
+bool operator==(const QRhiScissor &a, const QRhiScissor &b) Q_DECL_NOTHROW
+{
+ return a.scissor() == b.scissor();
+}
+
+/*!
+ \return \c false if the values in the two QRhiScissor
+ objects \a a and \a b are equal; otherwise returns \c true.
+
+ \relates QRhiScissor
+*/
+bool operator!=(const QRhiScissor &a, const QRhiScissor &b) Q_DECL_NOTHROW
+{
+ return !(a == b);
+}
+
+/*!
+ \return the hash value for \a v, using \a seed to seed the calculation.
+
+ \relates QRhiScissor
+ */
+uint qHash(const QRhiScissor &v, uint seed) Q_DECL_NOTHROW
+{
+ const std::array<int, 4> r = v.scissor();
+ return seed + r[0] + r[1] + r[2] + r[3];
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug dbg, const QRhiScissor &s)
+{
+ QDebugStateSaver saver(dbg);
+ const std::array<int, 4> r = s.scissor();
+ dbg.nospace() << "QRhiScissor(bottom-left-x=" << r[0]
+ << " bottom-left-y=" << r[1]
+ << " width=" << r[2]
+ << " height=" << r[3]
+ << ')';
+ return dbg;
+}
+#endif
+
+/*!
+ \class QRhiVertexInputBinding
+ \inmodule QtRhi
+ \brief Describes a vertex input binding.
+
+ Specifies the stride (in bytes, must be a multiple of 4), the
+ classification and optionally the instance step rate.
+
+ As an example, assume a vertex shader with the following inputs:
+
+ \badcode
+ layout(location = 0) in vec4 position;
+ layout(location = 1) in vec2 texcoord;
+ \endcode
+
+ Now let's assume also that 3 component vertex positions \c{(x, y, z)} and 2
+ component texture coordinates \c{(u, v)} are provided in a non-interleaved
+ format in a buffer (or separate buffers even). Definining two bindings
+ could then be done like this:
+
+ \badcode
+ QRhiVertexInputLayout inputLayout;
+ inputLayout.setBindings({
+ { 3 * sizeof(float) },
+ { 2 * sizeof(float) }
+ });
+ \endcode
+
+ Only the stride is interesting here since instancing is not used. The
+ binding number is given by the index of the QRhiVertexInputBinding
+ element in the bindings vector of the QRhiVertexInputLayout.
+
+ Once a graphics pipeline with this vertex input layout is bound, the vertex
+ inputs could be set up like the following for drawing a cube with 36
+ vertices, assuming we have a single buffer with first the positions and
+ then the texture coordinates:
+
+ \badcode
+ const QRhiCommandBuffer::VertexInput vbufBindings[] = {
+ { cubeBuf, 0 },
+ { cubeBuf, 36 * 3 * sizeof(float) }
+ };
+ cb->setVertexInput(0, 2, vbufBindings);
+ \endcode
+
+ Note how the index defined by \c {startBinding + i}, where \c i is the
+ index in the second argument of
+ \l{QRhiCommandBuffer::setVertexInput()}{setVertexInput()}, matches the
+ index of the corresponding entry in the \c bindings vector of the
+ QRhiVertexInputLayout.
+
+ \note the stride must always be a multiple of 4.
+
+ \sa QRhiCommandBuffer::setVertexInput()
+ */
+
+/*!
+ \enum QRhiVertexInputBinding::Classification
+ Describes the input data classification.
+
+ \value PerVertex Data is per-vertex
+ \value PerInstance Data is per-instance
+ */
+
+/*!
+ \fn QRhiVertexInputBinding::QRhiVertexInputBinding()
+
+ Constructs a default vertex input binding description.
+ */
+
+/*!
+ Constructs a vertex input binding description with the specified \a stride,
+ classification \a cls, and instance step rate \a stepRate.
+
+ \note \a stepRate other than 1 is only supported when
+ QRhi::CustomInstanceStepRate is reported to be supported.
+ */
+QRhiVertexInputBinding::QRhiVertexInputBinding(quint32 stride, Classification cls, int stepRate)
+ : m_stride(stride),
+ m_classification(cls),
+ m_instanceStepRate(stepRate)
+{
+}
+
+/*!
+ \return \c true if the values in the two QRhiVertexInputBinding objects
+ \a a and \a b are equal.
+
+ \relates QRhiVertexInputBinding
+ */
+bool operator==(const QRhiVertexInputBinding &a, const QRhiVertexInputBinding &b) Q_DECL_NOTHROW
+{
+ return a.stride() == b.stride()
+ && a.classification() == b.classification()
+ && a.instanceStepRate() == b.instanceStepRate();
+}
+
+/*!
+ \return \c false if the values in the two QRhiVertexInputBinding
+ objects \a a and \a b are equal; otherwise returns \c true.
+
+ \relates QRhiVertexInputBinding
+*/
+bool operator!=(const QRhiVertexInputBinding &a, const QRhiVertexInputBinding &b) Q_DECL_NOTHROW
+{
+ return !(a == b);
+}
+
+/*!
+ \return the hash value for \a v, using \a seed to seed the calculation.
+
+ \relates QRhiVertexInputBinding
+ */
+uint qHash(const QRhiVertexInputBinding &v, uint seed) Q_DECL_NOTHROW
+{
+ return seed + v.stride() + v.classification();
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug dbg, const QRhiVertexInputBinding &b)
+{
+ QDebugStateSaver saver(dbg);
+ dbg.nospace() << "QRhiVertexInputBinding(stride=" << b.stride()
+ << " cls=" << b.classification()
+ << " step-rate=" << b.instanceStepRate()
+ << ')';
+ return dbg;
+}
+#endif
+
+/*!
+ \class QRhiVertexInputAttribute
+ \inmodule QtRhi
+ \brief Describes a single vertex input element.
+
+ The members specify the binding number, location, format, and offset for a
+ single vertex input element.
+
+ \note For HLSL it is assumed that the vertex shader uses
+ \c{TEXCOORD<location>} as the semantic for each input. Hence no separate
+ semantic name and index.
+
+ As an example, assume a vertex shader with the following inputs:
+
+ \badcode
+ layout(location = 0) in vec4 position;
+ layout(location = 1) in vec2 texcoord;
+ \endcode
+
+ Now let's assume that we have 3 component vertex positions \c{(x, y, z)}
+ and 2 component texture coordinates \c{(u, v)} are provided in a
+ non-interleaved format in a buffer (or separate buffers even). Once two
+ bindings are defined, the attributes could be specified as:
+
+ \badcode
+ QRhiVertexInputLayout inputLayout;
+ inputLayout.setBindings({
+ { 3 * sizeof(float) },
+ { 2 * sizeof(float) }
+ });
+ inputLayout.setAttributes({
+ { 0, 0, QRhiVertexInputAttribute::Float3, 0 },
+ { 1, 1, QRhiVertexInputAttribute::Float2, 0 }
+ });
+ \endcode
+
+ Once a graphics pipeline with this vertex input layout is bound, the vertex
+ inputs could be set up like the following for drawing a cube with 36
+ vertices, assuming we have a single buffer with first the positions and
+ then the texture coordinates:
+
+ \badcode
+ const QRhiCommandBuffer::VertexInput vbufBindings[] = {
+ { cubeBuf, 0 },
+ { cubeBuf, 36 * 3 * sizeof(float) }
+ };
+ cb->setVertexInput(0, 2, vbufBindings);
+ \endcode
+
+ When working with interleaved data, there will typically be just one
+ binding, with multiple attributes referring to that same buffer binding
+ point:
+
+ \badcode
+ QRhiVertexInputLayout inputLayout;
+ inputLayout.setBindings({
+ { 5 * sizeof(float) }
+ });
+ inputLayout.setAttributes({
+ { 0, 0, QRhiVertexInputAttribute::Float3, 0 },
+ { 0, 1, QRhiVertexInputAttribute::Float2, 3 * sizeof(float) }
+ });
+ \endcode
+
+ and then:
+
+ \badcode
+ const QRhiCommandBuffer::VertexInput vbufBinding(interleavedCubeBuf, 0);
+ cb->setVertexInput(0, 1, &vbufBinding);
+ \endcode
+
+ \sa QRhiCommandBuffer::setVertexInput()
+ */
+
+/*!
+ \enum QRhiVertexInputAttribute::Format
+ Specifies the type of the element data.
+
+ \value Float4 Four component float vector
+ \value Float3 Three component float vector
+ \value Float2 Two component float vector
+ \value Float Float
+ \value UNormByte4 Four component normalized unsigned byte vector
+ \value UNormByte2 Two component normalized unsigned byte vector
+ \value UNormByte Normalized unsigned byte
+ */
+
+/*!
+ \fn QRhiVertexInputAttribute::QRhiVertexInputAttribute()
+
+ Constructs a default vertex input attribute description.
+ */
+
+/*!
+ Constructs a vertex input attribute description with the specified \a
+ binding number, \a location, \a format, and \a offset.
+ */
+QRhiVertexInputAttribute::QRhiVertexInputAttribute(int binding, int location, Format format, quint32 offset)
+ : m_binding(binding),
+ m_location(location),
+ m_format(format),
+ m_offset(offset)
+{
+}
+
+/*!
+ \return \c true if the values in the two QRhiVertexInputAttribute objects
+ \a a and \a b are equal.
+
+ \relates QRhiVertexInputAttribute
+ */
+bool operator==(const QRhiVertexInputAttribute &a, const QRhiVertexInputAttribute &b) Q_DECL_NOTHROW
+{
+ return a.binding() == b.binding()
+ && a.location() == b.location()
+ && a.format() == b.format()
+ && a.offset() == b.offset();
+}
+
+/*!
+ \return \c false if the values in the two QRhiVertexInputAttribute
+ objects \a a and \a b are equal; otherwise returns \c true.
+
+ \relates QRhiVertexInputAttribute
+*/
+bool operator!=(const QRhiVertexInputAttribute &a, const QRhiVertexInputAttribute &b) Q_DECL_NOTHROW
+{
+ return !(a == b);
+}
+
+/*!
+ \return the hash value for \a v, using \a seed to seed the calculation.
+
+ \relates QRhiVertexInputAttribute
+ */
+uint qHash(const QRhiVertexInputAttribute &v, uint seed) Q_DECL_NOTHROW
+{
+ return seed + v.binding() + v.location() + v.format() + v.offset();
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug dbg, const QRhiVertexInputAttribute &a)
+{
+ QDebugStateSaver saver(dbg);
+ dbg.nospace() << "QRhiVertexInputAttribute(binding=" << a.binding()
+ << " location=" << a.location()
+ << " format=" << a.format()
+ << " offset=" << a.offset()
+ << ')';
+ return dbg;
+}
+#endif
+
+/*!
+ \class QRhiVertexInputLayout
+ \inmodule QtRhi
+ \brief Describes the layout of vertex inputs consumed by a vertex shader.
+
+ The vertex input layout is defined by the collections of
+ QRhiVertexInputBinding and QRhiVertexInputAttribute.
+ */
+
+/*!
+ \fn QRhiVertexInputLayout::QRhiVertexInputLayout()
+
+ Constructs an empty vertex input layout description.
+ */
+
+/*!
+ \return \c true if the values in the two QRhiVertexInputLayout objects
+ \a a and \a b are equal.
+
+ \relates QRhiVertexInputLayout
+ */
+bool operator==(const QRhiVertexInputLayout &a, const QRhiVertexInputLayout &b) Q_DECL_NOTHROW
+{
+ return a.bindings() == b.bindings()
+ && a.attributes() == b.attributes();
+}
+
+/*!
+ \return \c false if the values in the two QRhiVertexInputLayout
+ objects \a a and \a b are equal; otherwise returns \c true.
+
+ \relates QRhiVertexInputLayout
+*/
+bool operator!=(const QRhiVertexInputLayout &a, const QRhiVertexInputLayout &b) Q_DECL_NOTHROW
+{
+ return !(a == b);
+}
+
+/*!
+ \return the hash value for \a v, using \a seed to seed the calculation.
+
+ \relates QRhiVertexInputLayout
+ */
+uint qHash(const QRhiVertexInputLayout &v, uint seed) Q_DECL_NOTHROW
+{
+ return qHash(v.bindings(), seed) + qHash(v.attributes(), seed);
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug dbg, const QRhiVertexInputLayout &v)
+{
+ QDebugStateSaver saver(dbg);
+ dbg.nospace() << "QRhiVertexInputLayout(bindings=" << v.bindings()
+ << " attributes=" << v.attributes()
+ << ')';
+ return dbg;
+}
+#endif
+
+/*!
+ \class QRhiGraphicsShaderStage
+ \inmodule QtRhi
+ \brief Specifies the type and the shader code for a shader stage in the graphics pipeline.
+ */
+
+/*!
+ \enum QRhiGraphicsShaderStage::Type
+ Specifies the type of the shader stage.
+
+ \value Vertex Vertex stage
+ \value Fragment Fragment (pixel) stage
+ */
+
+/*!
+ \fn QRhiGraphicsShaderStage::QRhiGraphicsShaderStage()
+
+ Constructs a shader stage description for the vertex stage with an empty
+ QShader.
+ */
+
+/*!
+ Constructs a shader stage description with the \a type of the stage and the
+ \a shader.
+
+ The shader variant \a v defaults to QShader::StandardShader. A
+ QShader contains multiple source and binary versions of a shader.
+ In addition, it can also contain variants of the shader with slightly
+ modified code. \a v can then be used to select the desired variant.
+ */
+QRhiGraphicsShaderStage::QRhiGraphicsShaderStage(Type type, const QShader &shader, QShader::Variant v)
+ : m_type(type),
+ m_shader(shader),
+ m_shaderVariant(v)
+{
+}
+
+/*!
+ \return \c true if the values in the two QRhiGraphicsShaderStage objects
+ \a a and \a b are equal.
+
+ \relates QRhiGraphicsShaderStage
+ */
+bool operator==(const QRhiGraphicsShaderStage &a, const QRhiGraphicsShaderStage &b) Q_DECL_NOTHROW
+{
+ return a.type() == b.type()
+ && a.shader() == b.shader()
+ && a.shaderVariant() == b.shaderVariant();
+}
+
+/*!
+ \return \c false if the values in the two QRhiGraphicsShaderStage
+ objects \a a and \a b are equal; otherwise returns \c true.
+
+ \relates QRhiGraphicsShaderStage
+*/
+bool operator!=(const QRhiGraphicsShaderStage &a, const QRhiGraphicsShaderStage &b) Q_DECL_NOTHROW
+{
+ return !(a == b);
+}
+
+/*!
+ \return the hash value for \a v, using \a seed to seed the calculation.
+
+ \relates QRhiGraphicsShaderStage
+ */
+uint qHash(const QRhiGraphicsShaderStage &v, uint seed) Q_DECL_NOTHROW
+{
+ return v.type() + qHash(v.shader(), seed) + v.shaderVariant();
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug dbg, const QRhiGraphicsShaderStage &s)
+{
+ QDebugStateSaver saver(dbg);
+ dbg.nospace() << "QRhiGraphicsShaderStage(type=" << s.type()
+ << " shader=" << s.shader()
+ << " variant=" << s.shaderVariant()
+ << ')';
+ return dbg;
+}
+#endif
+
+/*!
+ \class QRhiColorAttachment
+ \inmodule QtRhi
+ \brief Describes the a single color attachment of a render target.
+
+ A color attachment is either a QRhiTexture or a QRhiRenderBuffer. The
+ former, when texture() is set, is used in most cases.
+
+ \note texture() and renderBuffer() cannot be both set (be non-null at the
+ same time).
+
+ Setting renderBuffer instead is recommended only when multisampling is
+ needed. Relying on QRhi::MultisampleRenderBuffer is a better choice than
+ QRhi::MultisampleTexture in practice since the former is available in more
+ run time configurations (e.g. when running on OpenGL ES 3.0 which has no
+ support for multisample textures, but does support multisample
+ renderbuffers).
+
+ When targeting a non-multisample texture, the layer() and level()
+ indicate the targeted layer (face index \c{0-5} for cubemaps) and mip
+ level.
+
+ When texture() or renderBuffer() is multisample, resolveTexture() can be
+ set optionally. When set, samples are resolved automatically into that
+ (non-multisample) texture at the end of the render pass. When rendering
+ into a multisample renderbuffers, this is the only way to get resolved,
+ non-multisample content out of them. Multisample textures allow sampling in
+ shaders so for them this is just one option.
+
+ \note when resolving is enabled, the multisample data may not be written
+ out at all. This means that the multisample texture() must not be used
+ afterwards with shaders for sampling when resolveTexture() is set.
+ */
+
+/*!
+ \fn QRhiColorAttachment::QRhiColorAttachment()
+
+ Constructs an empty color attachment description.
+ */
+
+/*!
+ Constructs a color attachment description that specifies \a texture as the
+ associated color buffer.
+ */
+QRhiColorAttachment::QRhiColorAttachment(QRhiTexture *texture)
+ : m_texture(texture)
+{
+}
+
+/*!
+ Constructs a color attachment description that specifies \a renderBuffer as
+ the associated color buffer.
+ */
+QRhiColorAttachment::QRhiColorAttachment(QRhiRenderBuffer *renderBuffer)
+ : m_renderBuffer(renderBuffer)
+{
+}
+
+/*!
+ \class QRhiTextureRenderTargetDescription
+ \inmodule QtRhi
+ \brief Describes the color and depth or depth/stencil attachments of a render target.
+
+ A texture render target has zero or more textures as color attachments,
+ zero or one renderbuffer as combined depth/stencil buffer or zero or one
+ texture as depth buffer.
+
+ \note depthStencilBuffer() and depthTexture() cannot be both set (cannot be
+ non-null at the same time).
+ */
+
+/*!
+ \fn QRhiTextureRenderTargetDescription::QRhiTextureRenderTargetDescription()
+
+ Constructs an empty texture render target description.
+ */
+
+/*!
+ Constructs a texture render target description with one attachment
+ described by \a colorAttachment.
+ */
+QRhiTextureRenderTargetDescription::QRhiTextureRenderTargetDescription(const QRhiColorAttachment &colorAttachment)
+{
+ m_colorAttachments.append(colorAttachment);
+}
+
+/*!
+ Constructs a texture render target description with two attachments, a
+ color attachment described by \a colorAttachment, and a depth/stencil
+ attachment with \a depthStencilBuffer.
+ */
+QRhiTextureRenderTargetDescription::QRhiTextureRenderTargetDescription(const QRhiColorAttachment &colorAttachment,
+ QRhiRenderBuffer *depthStencilBuffer)
+ : m_depthStencilBuffer(depthStencilBuffer)
+{
+ m_colorAttachments.append(colorAttachment);
+}
+
+/*!
+ Constructs a texture render target description with two attachments, a
+ color attachment described by \a colorAttachment, and a depth attachment
+ with \a depthTexture.
+
+ \note \a depthTexture must have a suitable format, such as QRhiTexture::D16
+ or QRhiTexture::D32F.
+ */
+QRhiTextureRenderTargetDescription::QRhiTextureRenderTargetDescription(const QRhiColorAttachment &colorAttachment,
+ QRhiTexture *depthTexture)
+ : m_depthTexture(depthTexture)
+{
+ m_colorAttachments.append(colorAttachment);
+}
+
+/*!
+ \class QRhiTextureSubresourceUploadDescription
+ \inmodule QtRhi
+ \brief Describes the source for one mip level in a layer in a texture upload operation.
+
+ The source content is specified either as a QImage or as a raw blob. The
+ former is only allowed for uncompressed textures with a format that can be
+ mapped to QImage, while the latter is supported for all formats, including
+ floating point and compressed.
+
+ \note image() and data() cannot be both set at the same time.
+
+ destinationTopLeft() specifies the top-left corner of the target
+ rectangle. Defaults to (0, 0).
+
+ An empty sourceSize() (the default) indicates that size is assumed to be
+ the size of the subresource. With QImage-based uploads this implies that
+ the size of the source image() must match the subresource. When providing
+ raw data instead, sufficient number of bytes must be provided in data().
+
+ \note With compressed textures the first upload must always match the
+ subresource size due to graphics API limitations with some backends.
+
+ sourceTopLeft() is supported only for QImage-based uploads, and specifies
+ the top-left corner of the source rectangle.
+
+ \note Setting sourceSize() or sourceTopLeft() may trigger a QImage copy
+ internally, depending on the format and the backend.
+
+ When providing raw data, the stride (row pitch, row length in bytes) of the
+ provided data must be equal to \c{width * pixelSize} where \c pixelSize is
+ the number of bytes used for one pixel, and there must be no additional
+ padding between rows. There is no row start alignment requirement.
+
+ \note The format of the source data must be compatible with the texture
+ format. With many graphics APIs the data is copied as-is into a staging
+ buffer, there is no intermediate format conversion provided by QRhi. This
+ applies to floating point formats as well, with, for example, RGBA16F
+ requiring half floats in the source data.
+ */
+
+/*!
+ \fn QRhiTextureSubresourceUploadDescription::QRhiTextureSubresourceUploadDescription()
+
+ Constructs an empty subresource description.
+
+ \note an empty QRhiTextureSubresourceUploadDescription is not useful on its
+ own and should not be submitted to a QRhiTextureUploadEntry. At minimum
+ image or data must be set first.
+ */
+
+/*!
+ Constructs a mip level description with a \a image.
+
+ The \l{QImage::size()}{size} of \a image must match the size of the mip
+ level. For level 0 that is the \l{QRhiTexture::pixelSize()}{texture size}.
+
+ The bit depth of \a image must be compatible with the
+ \l{QRhiTexture::Format}{texture format}.
+
+ To describe a partial upload, call setSourceSize(), setSourceTopLeft(), or
+ setDestinationTopLeft() afterwards.
+ */
+QRhiTextureSubresourceUploadDescription::QRhiTextureSubresourceUploadDescription(const QImage &image)
+ : m_image(image)
+{
+}
+
+/*!
+ Constructs a mip level description with the image data is specified by \a
+ data and \a size. This is suitable for floating point and compressed
+ formats as well.
+
+ \a data can safely be destroyed or changed once this function returns.
+ */
+QRhiTextureSubresourceUploadDescription::QRhiTextureSubresourceUploadDescription(const void *data, int size)
+ : m_data(reinterpret_cast<const char *>(data), size)
+{
+}
+
+/*!
+ \class QRhiTextureUploadEntry
+ \inmodule QtRhi
+ \brief Describes one layer (face for cubemaps) in a texture upload operation.
+ */
+
+/*!
+ \fn QRhiTextureUploadEntry::QRhiTextureUploadEntry()
+
+ Constructs an empty QRhiTextureUploadEntry targeting layer 0 and level 0.
+
+ \note an empty QRhiTextureUploadEntry should not be submitted without
+ setting a QRhiTextureSubresourceUploadDescription via setDescription()
+ first.
+ */
+
+/*!
+ Constructs a QRhiTextureUploadEntry targeting the given \a layer and mip
+ \a level, with the subresource contents described by \a desc.
+ */
+QRhiTextureUploadEntry::QRhiTextureUploadEntry(int layer, int level,
+ const QRhiTextureSubresourceUploadDescription &desc)
+ : m_layer(layer),
+ m_level(level),
+ m_desc(desc)
+{
+}
+
+/*!
+ \class QRhiTextureUploadDescription
+ \inmodule QtRhi
+ \brief Describes a texture upload operation.
+
+ Used with QRhiResourceUpdateBatch::uploadTexture(). That function has two
+ variants: one taking a QImage and one taking a
+ QRhiTextureUploadDescription. The former is a convenience version,
+ internally creating a QRhiTextureUploadDescription with a single image
+ targeting level 0 for layer 0. However, when cubemaps, pre-generated mip
+ images, or compressed textures are involved, applications will have to work
+ directly with this class instead.
+
+ QRhiTextureUploadDescription also enables specifying batched uploads, which
+ are useful for example when generating an atlas or glyph cache texture:
+ multiple, partial uploads for the same subresource (meaning the same layer
+ and level) are supported, and can be, depending on the backend and the
+ underlying graphics API, more efficient when batched into the same
+ QRhiTextureUploadDescription as opposed to issuing individual
+ \l{QRhiResourceUpdateBatch::uploadTexture()}{uploadTexture()} commands for
+ each of them.
+
+ \note Cubemaps have one layer for each of the six faces in the order +X,
+ -X, +Y, -Y, +Z, -Z.
+
+ For example, specifying the faces of a cubemap could look like the following:
+
+ \badcode
+ QImage faces[6];
+ ...
+ QVector<QRhiTextureUploadEntry> entries;
+ for (int i = 0; i < 6; ++i)
+ entries.append(QRhiTextureUploadEntry(i, 0, faces[i]));
+ QRhiTextureUploadDescription desc(entries);
+ resourceUpdates->uploadTexture(texture, desc);
+ \endcode
+
+ Another example that specifies mip images for a compressed texture:
+
+ \badcode
+ QRhiTextureUploadDescription desc;
+ const int mipCount = rhi->mipLevelsForSize(compressedTexture->pixelSize());
+ for (int level = 0; level < mipCount; ++level) {
+ const QByteArray compressedDataForLevel = ..
+ desc.append(QRhiTextureUploadEntry(0, level, compressedDataForLevel));
+ }
+ resourceUpdates->uploadTexture(compressedTexture, desc);
+ \endcode
+
+ With partial uploads targeting the same subresource, it is recommended to
+ batch them into a single upload request, whenever possible:
+
+ \badcode
+ QRhiTextureSubresourceUploadDescription subresDesc(image);
+ subresDesc.setSourceSize(QSize(10, 10));
+ subResDesc.setDestinationTopLeft(QPoint(50, 40));
+ QRhiTextureUploadEntry entry(0, 0, subresDesc); // layer 0, level 0
+
+ QRhiTextureSubresourceUploadDescription subresDesc2(image);
+ subresDesc2.setSourceSize(QSize(30, 40));
+ subResDesc2.setDestinationTopLeft(QPoint(100, 200));
+ QRhiTextureUploadEntry entry2(0, 0, subresDesc2); // layer 0, level 0, i.e. same subresource
+
+ QRhiTextureUploadDescription desc({ entry, entry2});
+ resourceUpdates->uploadTexture(texture, desc);
+ \endcode
+ */
+
+/*!
+ \fn QRhiTextureUploadDescription::QRhiTextureUploadDescription()
+
+ Constructs an empty texture upload description.
+ */
+
+/*!
+ Constructs a texture upload description with a single subresource upload
+ described by \a entry.
+ */
+QRhiTextureUploadDescription::QRhiTextureUploadDescription(const QRhiTextureUploadEntry &entry)
+{
+ m_entries.append(entry);
+}
+
+/*!
+ Constructs a texture upload description with the specified list of \a entries.
+
+ \note \a entries can also contain multiple QRhiTextureUploadEntry elements
+ with the the same layer and level. This makes sense when those uploads are
+ partial, meaning their subresource description has a source size or image
+ smaller than the subresource dimensions, and can be more efficient than
+ issuing separate uploadTexture()'s.
+ */
+QRhiTextureUploadDescription::QRhiTextureUploadDescription(const QVector<QRhiTextureUploadEntry> &entries)
+ : m_entries(entries)
+{
+}
+
+/*!
+ Adds \a entry to the list of subresource uploads.
+ */
+void QRhiTextureUploadDescription::append(const QRhiTextureUploadEntry &entry)
+{
+ m_entries.append(entry);
+}
+
+/*!
+ \class QRhiTextureCopyDescription
+ \inmodule QtRhi
+ \brief Describes a texture-to-texture copy operation.
+
+ An empty pixelSize() indicates that the entire subresource is to be copied.
+ A default constructed copy description therefore leads to copying the
+ entire subresource at level 0 of layer 0.
+
+ \note The source texture must be created with
+ QRhiTexture::UsedAsTransferSource.
+
+ \note The source and destination rectangles defined by pixelSize(),
+ sourceTopLeft(), and destinationTopLeft() must fit the source and
+ destination textures, respectively. The behavior is undefined otherwise.
+ */
+
+/*!
+ \fn QRhiTextureCopyDescription::QRhiTextureCopyDescription()
+
+ Constructs an empty texture copy description.
+ */
+
+/*!
+ \class QRhiReadbackDescription
+ \inmodule QtRhi
+ \brief Describes a readback (reading back texture contents from possibly GPU-only memory) operation.
+
+ The source of the readback operation is either a QRhiTexture or the
+ current backbuffer of the currently targeted QRhiSwapChain. When
+ texture() is not set, the swapchain is used. Otherwise the specified
+ QRhiTexture is treated as the source.
+
+ \note Textures used in readbacks must be created with
+ QRhiTexture::UsedAsTransferSource.
+
+ \note Swapchains used in readbacks must be created with
+ QRhiSwapChain::UsedAsTransferSource.
+
+ layer() and level() are only applicable when the source is a QRhiTexture.
+
+ \note Multisample textures cannot be read back. Readbacks are supported for
+ multisample swapchain buffers however.
+ */
+
+/*!
+ \fn QRhiReadbackDescription::QRhiReadbackDescription()
+
+ Constructs an empty texture readback description.
+
+ \note The source texture is set to null by default, which is still a valid
+ readback: it specifies that the backbuffer of the current swapchain is to
+ be read back. (current meaning the frame's target swapchain at the time of
+ committing the QRhiResourceUpdateBatch with the
+ \l{QRhiResourceUpdateBatch::readBackTexture()}{texture readback} on it)
+ */
+
+/*!
+ Constructs an texture readback description that specifies that level 0 of
+ layer 0 of \a texture is to be read back.
+
+ \note \a texture can also be null in which case this constructor is
+ identical to the argumentless variant.
+ */
+QRhiReadbackDescription::QRhiReadbackDescription(QRhiTexture *texture)
+ : m_texture(texture)
+{
+}
+
+/*!
+ \class QRhiReadbackResult
+ \inmodule QtRhi
+ \brief Describes the results of a potentially asynchronous readback operation.
+
+ When \l completed is set, the function is invoked when the \l data is
+ available. \l format and \l pixelSize are set upon completion together with
+ \l data.
+ */
+
+/*!
+ \class QRhiNativeHandles
+ \inmodule QtRhi
+ \brief Base class for classes exposing backend-specific collections of native resource objects.
+ */
+
+/*!
+ \class QRhiResource
+ \inmodule QtRhi
+ \brief Base class for classes encapsulating native resource objects.
+ */
+
+/*!
+ \fn QRhiResource::Type QRhiResource::resourceType() const
+
+ \return the type of the resource.
+ */
+
+/*!
+ \internal
+ */
+QRhiResource::QRhiResource(QRhiImplementation *rhi)
+ : m_rhi(rhi)
+{
+ m_id = QRhiGlobalObjectIdGenerator::newId();
+}
+
+/*!
+ Destructor.
+
+ Releases (or requests deferred releasing of) the underlying native graphics
+ resources, if there are any.
+
+ \note Resources referenced by commands for the current frame should not be
+ released until the frame is submitted by QRhi::endFrame().
+
+ \sa release()
+ */
+QRhiResource::~QRhiResource()
+{
+ // release() cannot be called here, it being virtual; it is up to the
+ // subclasses to do that.
+}
+
+/*!
+ \fn void QRhiResource::release()
+
+ Releases (or requests deferred releasing of) the underlying native graphics
+ resources. Safe to call multiple times, subsequent invocations will be a
+ no-op then.
+
+ Once release() is called, the QRhiResource instance can be reused, by
+ calling \c build() again. That will then result in creating new native
+ graphics resources underneath.
+
+ \note Resources referenced by commands for the current frame should not be
+ released until the frame is submitted by QRhi::endFrame().
+
+ The QRhiResource destructor also performs the same task, so calling this
+ function is not necessary before destroying a QRhiResource.
+
+ \sa releaseAndDestroyLater()
+ */
+
+/*!
+ When called without a frame being recorded, this function is equivalent to
+ deleting the object. Between a QRhi::beginFrame() and QRhi::endFrame()
+ however the behavior is different: the QRhiResource will not be destroyed
+ until the frame is submitted via QRhi::endFrame(), thus satisfying the QRhi
+ requirement of not altering QRhiResource objects that are referenced by the
+ frame being recorded.
+
+ \sa release()
+ */
+void QRhiResource::releaseAndDestroyLater()
+{
+ m_rhi->addReleaseAndDestroyLater(this);
+}
+
+/*!
+ \return the currently set object name. By default the name is empty.
+ */
+QByteArray QRhiResource::name() const
+{
+ return m_objectName;
+}
+
+/*!
+ Sets a \a name for the object.
+
+ This has two uses: to get descriptive names for the native graphics
+ resources visible in graphics debugging tools, such as
+ \l{https://renderdoc.org/}{RenderDoc} and
+ \l{https://developer.apple.com/xcode/}{XCode}, and in the output stream of
+ QRhiProfiler.
+
+ When it comes to naming native objects by relaying the name via the
+ appropriate graphics API, note that the name is ignored when
+ QRhi::DebugMarkers are not supported, and may, depending on the backend,
+ also be ignored when QRhi::EnableDebugMarkers is not set.
+
+ \note The name may be ignored for objects other than buffers,
+ renderbuffers, and textures, depending on the backend.
+
+ \note The name may be modified. For slotted resources, such as a QRhiBuffer
+ backed by multiple native buffers, QRhi will append a suffix to make the
+ underlying native buffers easily distinguishable from each other.
+ */
+void QRhiResource::setName(const QByteArray &name)
+{
+ m_objectName = name;
+ m_objectName.replace(',', '_'); // cannot contain comma for QRhiProfiler
+}
+
+/*!
+ \return the global, unique identifier of this QRhiResource.
+
+ User code rarely needs to deal with the value directly. It is used
+ internally for tracking and bookkeeping purposes.
+ */
+quint64 QRhiResource::globalResourceId() const
+{
+ return m_id;
+}
+
+/*!
+ \class QRhiBuffer
+ \inmodule QtRhi
+ \brief Vertex, index, or uniform (constant) buffer resource.
+ */
+
+/*!
+ \enum QRhiBuffer::Type
+ Specifies storage type of buffer resource.
+
+ \value Immutable Indicates that the data is not expected to change ever
+ after the initial upload. Under the hood such buffer resources are
+ typically placed in device local (GPU) memory (on systems where
+ applicable). Uploading new data is possible, but may be expensive. The
+ upload typically happens by copying to a separate, host visible staging
+ buffer from which a GPU buffer-to-buffer copy is issued into the actual
+ GPU-only buffer.
+
+ \value Static Indicates that the data is expected to change only
+ infrequently. Typically placed in device local (GPU) memory, where
+ applicable. On backends where host visible staging buffers are used for
+ uploading, the staging buffers are kept around for this type, unlike with
+ Immutable, so subsequent uploads do not suffer in performance. Frequent
+ updates, especially updates in consecutive frames, should be avoided.
+
+ \value Dynamic Indicates that the data is expected to change frequently.
+ Not recommended for large buffers. Typically backed by host visible memory
+ in 2 copies in order to allow for changing without stalling the graphics
+ pipeline. The double buffering is managed transparently to the applications
+ and is not exposed in the API here in any form. This is the recommended,
+ and, with some backends, the only possible, type for buffers with
+ UniformBuffer usage.
+ */
+
+/*!
+ \enum QRhiBuffer::UsageFlag
+ Flag values to specify how the buffer is going to be used.
+
+ \value VertexBuffer Vertex buffer
+ \value IndexBuffer Index buffer
+ \value UniformBuffer Uniform (constant) buffer
+ */
+
+/*!
+ \fn void QRhiBuffer::setSize(int sz)
+
+ Sets the size of the buffer in bytes. The size is normally specified in
+ QRhi::newBuffer() so this function is only used when the size has to be
+ changed. As with other setters, the size only takes effect when calling
+ build(), and for already built buffers this involves releasing the previous
+ native resource and creating new ones under the hood.
+
+ Backends may choose to allocate buffers bigger than \a sz in order to
+ fulfill alignment requirements. This is hidden from the applications and
+ size() will always report the size requested in \a sz.
+ */
+
+/*!
+ \internal
+ */
+QRhiBuffer::QRhiBuffer(QRhiImplementation *rhi, Type type_, UsageFlags usage_, int size_)
+ : QRhiResource(rhi),
+ m_type(type_), m_usage(usage_), m_size(size_)
+{
+}
+
+/*!
+ \return the resource type.
+ */
+QRhiResource::Type QRhiBuffer::resourceType() const
+{
+ return Buffer;
+}
+
+/*!
+ \fn bool QRhiBuffer::build()
+
+ Creates the corresponding native graphics resources. If there are already
+ resources present due to an earlier build() with no corresponding
+ release(), then release() is called implicitly first.
+
+ \return \c true when successful, \c false when a graphics operation failed.
+ Regardless of the return value, calling release() is always safe.
+ */
+
+/*!
+ \class QRhiRenderBuffer
+ \inmodule QtRhi
+ \brief Renderbuffer resource.
+
+ Renderbuffers cannot be sampled or read but have some benefits over
+ textures in some cases:
+
+ A DepthStencil renderbuffer may be lazily allocated and be backed by
+ transient memory with some APIs. On some platforms this may mean the
+ depth/stencil buffer uses no physical backing at all.
+
+ Color renderbuffers are useful since QRhi::MultisampleRenderBuffer may be
+ supported even when QRhi::MultisampleTexture is not.
+
+ How the renderbuffer is implemented by a backend is not exposed to the
+ applications. In some cases it may be backed by ordinary textures, while in
+ others there may be a different kind of native resource used.
+ */
+
+/*!
+ \enum QRhiRenderBuffer::Type
+ Specifies the type of the renderbuffer
+
+ \value DepthStencil Combined depth/stencil
+ \value Color Color
+ */
+
+/*!
+ \enum QRhiRenderBuffer::Flag
+ Flag values for flags() and setFlags()
+
+ \value UsedWithSwapChainOnly For DepthStencil renderbuffers this indicates
+ that the renderbuffer is only used in combination with a QRhiSwapChain and
+ never in other ways. Relevant with some backends, while others ignore it.
+ With OpenGL where a separate windowing system interface API is in use (EGL,
+ GLX, etc.), the flag is important since it avoids creating any actual
+ resource as there is already a windowing system provided depth/stencil
+ buffer as requested by QSurfaceFormat.
+ */
+
+/*!
+ \internal
+ */
+QRhiRenderBuffer::QRhiRenderBuffer(QRhiImplementation *rhi, Type type_, const QSize &pixelSize_,
+ int sampleCount_, Flags flags_)
+ : QRhiResource(rhi),
+ m_type(type_), m_pixelSize(pixelSize_), m_sampleCount(sampleCount_), m_flags(flags_)
+{
+}
+
+/*!
+ \return the resource type.
+ */
+QRhiResource::Type QRhiRenderBuffer::resourceType() const
+{
+ return RenderBuffer;
+}
+
+/*!
+ \fn bool QRhiRenderBuffer::build()
+
+ Creates the corresponding native graphics resources. If there are already
+ resources present due to an earlier build() with no corresponding
+ release(), then release() is called implicitly first.
+
+ \return \c true when successful, \c false when a graphics operation failed.
+ Regardless of the return value, calling release() is always safe.
+ */
+
+/*!
+ \fn QRhiTexture::Format QRhiRenderBuffer::backingFormat() const
+
+ \internal
+ */
+
+/*!
+ \class QRhiTexture
+ \inmodule QtRhi
+ \brief Texture resource.
+ */
+
+/*!
+ \enum QRhiTexture::Flag
+
+ Flag values to specify how the texture is going to be used. Not honoring
+ the flags set before build() and attempting to use the texture in ways that
+ was not declared upfront can lead to unspecified behavior or decreased
+ performance depending on the backend and the underlying graphics API.
+
+ \value RenderTarget The texture going to be used in combination with
+ QRhiTextureRenderTarget.
+
+ \value CubeMap The texture is a cubemap. Such textures have 6 layers, one
+ for each face in the order of +X, -X, +Y, -Y, +Z, -Z. Cubemap textures
+ cannot be multisample.
+
+ \value MipMapped The texture has mipmaps. The appropriate mip count is
+ calculated automatically and can also be retrieved via
+ QRhi::mipLevelsForSize(). The images for the mip levels have to be
+ provided in the texture uploaded or generated via
+ QRhiResourceUpdateBatch::generateMips(). Multisample textures cannot have
+ mipmaps.
+
+ \value sRGB Use an sRGB format.
+
+ \value UsedAsTransferSource The texture is used as the source of a texture
+ copy or readback, meaning the texture is given as the source in
+ QRhiResourceUpdateBatch::copyTexture() or
+ QRhiResourceUpdateBatch::readBackTexture().
+
+ \value UsedWithGenerateMips The texture is going to be used with
+ QRhiResourceUpdateBatch::generateMips().
+ */
+
+/*!
+ \enum QRhiTexture::Format
+
+ Specifies the texture format. See also QRhi::isTextureFormatSupported() and
+ note that flags() can modify the format when QRhiTexture::sRGB is set.
+
+ \value UnknownFormat Not a valid format. This cannot be passed to setFormat().
+
+ \value RGBA8 Four component, unsigned normalized 8 bit per component. Always supported.
+
+ \value BGRA8 Four component, unsigned normalized 8 bit per component.
+
+ \value R8 One component, unsigned normalized 8 bit.
+
+ \value R16 One component, unsigned normalized 16 bit.
+
+ \value RED_OR_ALPHA8 Either same as R8, or is a similar format with the component swizzled to alpha,
+ depending on \l{QRhi::RedOrAlpha8IsRed}{RedOrAlpha8IsRed}.
+
+ \value RGBA16F Four components, 16-bit float per component.
+
+ \value RGBA32F Four components, 32-bit float per component.
+
+ \value D16 16-bit depth (normalized unsigned integer)
+
+ \value D32F 32-bit depth (32-bit float)
+
+ \value BC1
+ \value BC2
+ \value BC3
+ \value BC4
+ \value BC5
+ \value BC6H
+ \value BC7
+
+ \value ETC2_RGB8
+ \value ETC2_RGB8A1
+ \value ETC2_RGBA8
+
+ \value ASTC_4x4
+ \value ASTC_5x4
+ \value ASTC_5x5
+ \value ASTC_6x5
+ \value ASTC_6x6
+ \value ASTC_8x5
+ \value ASTC_8x6
+ \value ASTC_8x8
+ \value ASTC_10x5
+ \value ASTC_10x6
+ \value ASTC_10x8
+ \value ASTC_10x10
+ \value ASTC_12x10
+ \value ASTC_12x12
+ */
+
+/*!
+ \internal
+ */
+QRhiTexture::QRhiTexture(QRhiImplementation *rhi, Format format_, const QSize &pixelSize_,
+ int sampleCount_, Flags flags_)
+ : QRhiResource(rhi),
+ m_format(format_), m_pixelSize(pixelSize_), m_sampleCount(sampleCount_), m_flags(flags_)
+{
+}
+
+/*!
+ \return the resource type.
+ */
+QRhiResource::Type QRhiTexture::resourceType() const
+{
+ return Texture;
+}
+
+/*!
+ \fn bool QRhiTexture::build()
+
+ Creates the corresponding native graphics resources. If there are already
+ resources present due to an earlier build() with no corresponding
+ release(), then release() is called implicitly first.
+
+ \return \c true when successful, \c false when a graphics operation failed.
+ Regardless of the return value, calling release() is always safe.
+ */
+
+/*!
+ \return a pointer to a backend-specific QRhiNativeHandles subclass, such as
+ QRhiVulkanTextureNativeHandles. The returned value is null when exposing
+ the underlying native resources is not supported by the backend.
+
+ \sa QRhiVulkanTextureNativeHandles, QRhiD3D11TextureNativeHandles,
+ QRhiMetalTextureNativeHandles, QRhiGles2TextureNativeHandles
+ */
+const QRhiNativeHandles *QRhiTexture::nativeHandles()
+{
+ return nullptr;
+}
+
+/*!
+ Similar to build() except that no new native textures are created. Instead,
+ the texture from \a src is used.
+
+ This allows importing an existing native texture object (which must belong
+ to the same device or sharing context, depending on the graphics API) from
+ an external graphics engine.
+
+ \note format(), pixelSize(), sampleCount(), and flags() must still be set
+ correctly. Passing incorrect sizes and other values to QRhi::newTexture()
+ and then following it with a buildFrom() expecting that the native texture
+ object alone is sufficient to deduce such values is \b wrong and will lead
+ to problems.
+
+ \note QRhiTexture does not take ownership of the texture object. release()
+ does not free the object or any associated memory.
+
+ The opposite of this operation, exposing a QRhiTexture-created native
+ texture object to a foreign engine, is possible via nativeHandles().
+
+ \sa QRhiVulkanTextureNativeHandles, QRhiD3D11TextureNativeHandles,
+ QRhiMetalTextureNativeHandles, QRhiGles2TextureNativeHandles
+ */
+bool QRhiTexture::buildFrom(const QRhiNativeHandles *src)
+{
+ Q_UNUSED(src);
+ return false;
+}
+
+/*!
+ \class QRhiSampler
+ \inmodule QtRhi
+ \brief Sampler resource.
+ */
+
+/*!
+ \enum QRhiSampler::Filter
+ Specifies the minification, magnification, or mipmap filtering
+
+ \value None Applicable only for mipmapMode(), indicates no mipmaps to be used
+ \value Nearest
+ \value Linear
+ */
+
+/*!
+ \enum QRhiSampler::AddressMode
+ Specifies the addressing mode
+
+ \value Repeat
+ \value ClampToEdge
+ \value Border
+ \value Mirror
+ \value MirrorOnce
+ */
+
+/*!
+ \enum QRhiSampler::CompareOp
+ Specifies the texture comparison function.
+
+ \value Never (default)
+ \value Less
+ \value Equal
+ \value LessOrEqual
+ \value Greater
+ \value NotEqual
+ \value GreaterOrEqual
+ \value Always
+ */
+
+/*!
+ \internal
+ */
+QRhiSampler::QRhiSampler(QRhiImplementation *rhi,
+ Filter magFilter_, Filter minFilter_, Filter mipmapMode_,
+ AddressMode u_, AddressMode v_)
+ : QRhiResource(rhi),
+ m_magFilter(magFilter_), m_minFilter(minFilter_), m_mipmapMode(mipmapMode_),
+ m_addressU(u_), m_addressV(v_),
+ m_addressW(QRhiSampler::ClampToEdge),
+ m_compareOp(QRhiSampler::Never)
+{
+}
+
+/*!
+ \return the resource type.
+ */
+QRhiResource::Type QRhiSampler::resourceType() const
+{
+ return Sampler;
+}
+
+/*!
+ \class QRhiRenderPassDescriptor
+ \inmodule QtRhi
+ \brief Render pass resource.
+ */
+
+/*!
+ \internal
+ */
+QRhiRenderPassDescriptor::QRhiRenderPassDescriptor(QRhiImplementation *rhi)
+ : QRhiResource(rhi)
+{
+}
+
+/*!
+ \return the resource type.
+ */
+QRhiResource::Type QRhiRenderPassDescriptor::resourceType() const
+{
+ return RenderPassDescriptor;
+}
+
+/*!
+ \class QRhiRenderTarget
+ \inmodule QtRhi
+ \brief Represents an onscreen (swapchain) or offscreen (texture) render target.
+ */
+
+/*!
+ \internal
+ */
+QRhiRenderTarget::QRhiRenderTarget(QRhiImplementation *rhi)
+ : QRhiResource(rhi)
+{
+}
+
+/*!
+ \return the resource type.
+ */
+QRhiResource::Type QRhiRenderTarget::resourceType() const
+{
+ return RenderTarget;
+}
+
+/*!
+ \fn QSize QRhiRenderTarget::pixelSize() const
+
+ \return the size in pixels.
+ */
+
+/*!
+ \fn float QRhiRenderTarget::devicePixelRatio() const
+
+ \return the device pixel ratio. For QRhiTextureRenderTarget this is always
+ 1. For targets retrieved from a QRhiSwapChain the value reflects the
+ \l{QWindow::devicePixelRatio()}{device pixel ratio} of the targeted
+ QWindow.
+ */
+
+/*!
+ \class QRhiTextureRenderTarget
+ \inmodule QtRhi
+ \brief Texture render target resource.
+
+ A texture render target allows rendering into one or more textures,
+ optionally with a depth texture or depth/stencil renderbuffer.
+
+ \note Textures used in combination with QRhiTextureRenderTarget must be
+ created with the QRhiTexture::RenderTarget flag.
+
+ The simplest example of creating a render target with a texture as its
+ single color attachment:
+
+ \badcode
+ texture = rhi->newTexture(QRhiTexture::RGBA8, size, 1, QRhiTexture::RenderTarget);
+ texture->build();
+ rt = rhi->newTextureRenderTarget({ texture });
+ rp = rt->newCompatibleRenderPassDescriptor();
+ rt->setRenderPassDescriptor(rt);
+ rt->build();
+ // rt can now be used with beginPass()
+ \endcode
+ */
+
+/*!
+ \enum QRhiTextureRenderTarget::Flag
+
+ Flag values describing the load/store behavior for the render target. The
+ load/store behavior may be baked into native resources under the hood,
+ depending on the backend, and therefore it needs to be known upfront and
+ cannot be changed without rebuilding (and so releasing and creating new
+ native resources).
+
+ \value PreserveColorContents Indicates that the contents of the color
+ attachments is to be loaded when starting a render pass, instead of
+ clearing. This is potentially more expensive, especially on mobile (tiled)
+ GPUs, but allows preserving the existing contents between passes.
+
+ \value PreserveDepthStencilContents Indicates that the contents of the
+ depth texture is to be loaded when starting a render pass, instead
+ clearing. Only applicable when a texture is used as the depth buffer
+ (QRhiTextureRenderTargetDescription::depthTexture() is set) because
+ depth/stencil renderbuffers may not have any physical backing and data may
+ not be written out in the first place.
+ */
+
+/*!
+ \internal
+ */
+QRhiTextureRenderTarget::QRhiTextureRenderTarget(QRhiImplementation *rhi,
+ const QRhiTextureRenderTargetDescription &desc_,
+ Flags flags_)
+ : QRhiRenderTarget(rhi),
+ m_desc(desc_),
+ m_flags(flags_)
+{
+}
+
+/*!
+ \return the resource type.
+ */
+QRhiResource::Type QRhiTextureRenderTarget::resourceType() const
+{
+ return TextureRenderTarget;
+}
+
+/*!
+ \fn QRhiRenderPassDescriptor *QRhiTextureRenderTarget::newCompatibleRenderPassDescriptor()
+
+ \return a new QRhiRenderPassDescriptor that is compatible with this render
+ target.
+
+ The returned value is used in two ways: it can be passed to
+ setRenderPassDescriptor() and
+ QRhiGraphicsPipeline::setRenderPassDescriptor(). A render pass descriptor
+ describes the attachments (color, depth/stencil) and the load/store
+ behavior that can be affected by flags(). A QRhiGraphicsPipeline can only
+ be used in combination with a render target that has the same
+ QRhiRenderPassDescriptor set.
+
+ Two QRhiTextureRenderTarget instances can share the same render pass
+ descriptor as long as they have the same number and type of attachments.
+ The associated QRhiTexture or QRhiRenderBuffer instances are not part of
+ the render pass descriptor so those can differ in the two
+ QRhiTextureRenderTarget intances.
+
+ \note resources, such as QRhiTexture instances, referenced in description()
+ must already be built
+
+ \sa build()
+ */
+
+/*!
+ \fn bool QRhiTextureRenderTarget::build()
+
+ Creates the corresponding native graphics resources. If there are already
+ resources present due to an earlier build() with no corresponding
+ release(), then release() is called implicitly first.
+
+ \note renderPassDescriptor() must be set before calling build(). To obtain
+ a QRhiRenderPassDescriptor compatible with the render target, call
+ newCompatibleRenderPassDescriptor() before build() but after setting all
+ other parameters, such as description() and flags(). To save resources,
+ reuse the same QRhiRenderPassDescriptor with multiple
+ QRhiTextureRenderTarget instances, whenever possible. Sharing the same
+ render pass descriptor is only possible when the render targets have the
+ same number and type of attachments (the actual textures can differ) and
+ the same flags.
+
+ \note resources, such as QRhiTexture instances, referenced in description()
+ must already be built
+
+ \return \c true when successful, \c false when a graphics operation failed.
+ Regardless of the return value, calling release() is always safe.
+ */
+
+/*!
+ \class QRhiShaderResourceBindings
+ \inmodule QtRhi
+ \brief Encapsulates resources for making buffer, texture, sampler resources visible to shaders.
+
+ A QRhiShaderResourceBindings is a collection of QRhiShaderResourceBinding
+ objects, each of which describe a single binding.
+
+ Take a fragment shader with the following interface:
+
+ \badcode
+ layout(std140, binding = 0) uniform buf {
+ mat4 mvp;
+ int flip;
+ } ubuf;
+
+ layout(binding = 1) uniform sampler2D tex;
+ \endcode
+
+ To make resources visible to the shader, the following
+ QRhiShaderResourceBindings could be created and then passed to
+ QRhiGraphicsPipeline::setShaderResourceBindings():
+
+ \badcode
+ srb = rhi->newShaderResourceBindings();
+ srb->setBindings({
+ QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, ubuf),
+ QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, texture, sampler)
+ });
+ srb->build();
+ ...
+ ps = rhi->newGraphicsPipeline();
+ ...
+ ps->setShaderResourceBindings(srb);
+ ps->build();
+ ...
+ cb->setGraphicsPipeline(ps);
+ cb->setShaderResources(); // binds srb
+ \endcode
+
+ This assumes that \c ubuf is a QRhiBuffer, \c texture is a QRhiTexture,
+ while \a sampler is a QRhiSampler. The example also assumes that the
+ uniform block is present in the vertex shader as well so the same buffer is
+ made visible to the vertex stage too.
+
+ \section3 Advanced usage
+
+ Building on the above example, let's assume that a pass now needs to use
+ the exact same pipeline and shaders with a different texture. Creating a
+ whole separate QRhiGraphicsPipeline just for this would be an overkill.
+ This is why QRhiCommandBuffer::setShaderResources() allows specifying a \a
+ srb argument. As long as the layouts (so the number of bindings and the
+ binding points) match between two QRhiShaderResourceBindings, they can both
+ be used with the same pipeline, assuming the pipeline was built with one of
+ them in the first place.
+
+ Creating and then using a new \c srb2 that is very similar to \c srb with
+ the exception of referencing another texture could be implemented like the
+ following:
+
+ \badcode
+ srb2 = rhi->newShaderResourceBindings();
+ QVector<QRhiShaderResourceBinding> bindings = srb->bindings();
+ bindings[1] = QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, anotherTexture, sampler);
+ srb2->setBindings(bindings);
+ srb2->build();
+ ...
+ cb->setGraphicsPipeline(ps);
+ cb->setShaderResources(srb2); // binds srb2
+ \endcode
+ */
+
+/*!
+ \internal
+ */
+QRhiShaderResourceBindings::QRhiShaderResourceBindings(QRhiImplementation *rhi)
+ : QRhiResource(rhi)
+{
+}
+
+/*!
+ \return the resource type.
+ */
+QRhiResource::Type QRhiShaderResourceBindings::resourceType() const
+{
+ return ShaderResourceBindings;
+}
+
+/*!
+ \return \c true if the layout is compatible with \a other. The layout does
+ not include the actual resource (such as, buffer or texture) and related
+ parameters (such as, offset or size). It does include the binding point,
+ pipeline stage, and resource type, however. The number and order of the
+ bindings must also match in order to be compatible.
+
+ When there is a QRhiGraphicsPipeline created with this
+ QRhiShaderResourceBindings, and the function returns \c true, \a other can
+ then safely be passed to QRhiCommandBuffer::setShaderResources(), and so
+ be used with the pipeline in place of this QRhiShaderResourceBindings.
+
+ This function can be called before build() as well. The bindings must
+ already be set via setBindings() however.
+ */
+bool QRhiShaderResourceBindings::isLayoutCompatible(const QRhiShaderResourceBindings *other) const
+{
+ const int count = m_bindings.count();
+ if (count != other->m_bindings.count())
+ return false;
+
+ for (int i = 0; i < count; ++i) {
+ if (!m_bindings[i].isLayoutCompatible(other->m_bindings.at(i)))
+ return false;
+ }
+
+ return true;
+}
+
+/*!
+ \class QRhiShaderResourceBinding
+ \inmodule QtRhi
+ \brief Describes the shader resource for a single binding point.
+
+ A QRhiShaderResourceBinding cannot be constructed directly. Instead, use
+ the static functions uniformBuffer(), sampledTexture() to get an instance.
+ */
+
+/*!
+ \enum QRhiShaderResourceBinding::Type
+ Specifies type of the shader resource bound to a binding point
+
+ \value UniformBuffer Uniform buffer
+ \value SampledTexture Combined image sampler
+ */
+
+/*!
+ \enum QRhiShaderResourceBinding::StageFlag
+ Flag values to indicate which stages the shader resource is visible in
+
+ \value VertexStage Vertex stage
+ \value FragmentStage Fragment (pixel) stage
+ */
+
+/*!
+ \internal
+ */
+QRhiShaderResourceBinding::QRhiShaderResourceBinding()
+ : d(new QRhiShaderResourceBindingPrivate)
+{
+}
+
+/*!
+ \internal
+ */
+void QRhiShaderResourceBinding::detach()
+{
+ qAtomicDetach(d);
+}
+
+/*!
+ \internal
+ */
+QRhiShaderResourceBinding::QRhiShaderResourceBinding(const QRhiShaderResourceBinding &other)
+ : d(other.d)
+{
+ d->ref.ref();
+}
+
+/*!
+ \internal
+ */
+QRhiShaderResourceBinding &QRhiShaderResourceBinding::operator=(const QRhiShaderResourceBinding &other)
+{
+ qAtomicAssign(d, other.d);
+ return *this;
+}
+
+/*!
+ Destructor.
+ */
+QRhiShaderResourceBinding::~QRhiShaderResourceBinding()
+{
+ if (!d->ref.deref())
+ delete d;
+}
+
+/*!
+ \return \c true if the layout is compatible with \a other. The layout does not
+ include the actual resource (such as, buffer or texture) and related
+ parameters (such as, offset or size).
+
+ For example, \c a and \c b below are not equal, but are compatible layout-wise:
+
+ \badcode
+ auto a = QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage, buffer);
+ auto b = QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage, someOtherBuffer, 256);
+ \endcode
+ */
+bool QRhiShaderResourceBinding::isLayoutCompatible(const QRhiShaderResourceBinding &other) const
+{
+ return (d == other.d)
+ || (d->binding == other.d->binding && d->stage == other.d->stage && d->type == other.d->type);
+}
+
+/*!
+ \return a shader resource binding for the given binding number, pipeline
+ stages, and buffer specified by \a binding, \a stage, and \a buf.
+ */
+QRhiShaderResourceBinding QRhiShaderResourceBinding::uniformBuffer(
+ int binding, StageFlags stage, QRhiBuffer *buf)
+{
+ QRhiShaderResourceBinding b;
+ QRhiShaderResourceBindingPrivate *d = QRhiShaderResourceBindingPrivate::get(&b);
+ Q_ASSERT(d->ref.load() == 1);
+ d->binding = binding;
+ d->stage = stage;
+ d->type = UniformBuffer;
+ d->u.ubuf.buf = buf;
+ d->u.ubuf.offset = 0;
+ d->u.ubuf.maybeSize = 0; // entire buffer
+ d->u.ubuf.hasDynamicOffset = false;
+ return b;
+}
+
+/*!
+ \return a shader resource binding for the given binding number, pipeline
+ stages, and buffer specified by \a binding, \a stage, and \a buf. This
+ overload binds a region only, as specified by \a offset and \a size.
+
+ \note It is up to the user to ensure the offset is aligned to
+ QRhi::ubufAlignment().
+
+ \note \a size must be greater than 0.
+ */
+QRhiShaderResourceBinding QRhiShaderResourceBinding::uniformBuffer(
+ int binding, StageFlags stage, QRhiBuffer *buf, int offset, int size)
+{
+ Q_ASSERT(size > 0);
+ QRhiShaderResourceBinding b;
+ QRhiShaderResourceBindingPrivate *d = QRhiShaderResourceBindingPrivate::get(&b);
+ Q_ASSERT(d->ref.load() == 1);
+ d->binding = binding;
+ d->stage = stage;
+ d->type = UniformBuffer;
+ d->u.ubuf.buf = buf;
+ d->u.ubuf.offset = offset;
+ d->u.ubuf.maybeSize = size;
+ d->u.ubuf.hasDynamicOffset = false;
+ return b;
+}
+
+/*!
+ \return a shader resource binding for the given binding number, pipeline
+ stages, and buffer specified by \a binding, \a stage, and \a buf. The
+ uniform buffer is assumed to have dynamic offset. The dynamic offset can be
+ specified in QRhiCommandBuffer::setShaderResources(), thus allowing using
+ varying offset values without creating new bindings for the buffer. The
+ size of the bound region is specified by \a size. Like with non-dynamic
+ offsets, \c{offset + size} cannot exceed the size of \a buf.
+ */
+QRhiShaderResourceBinding QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(
+ int binding, StageFlags stage, QRhiBuffer *buf, int size)
+{
+ QRhiShaderResourceBinding b;
+ QRhiShaderResourceBindingPrivate *d = QRhiShaderResourceBindingPrivate::get(&b);
+ Q_ASSERT(d->ref.load() == 1);
+ d->binding = binding;
+ d->stage = stage;
+ d->type = UniformBuffer;
+ d->u.ubuf.buf = buf;
+ d->u.ubuf.offset = 0;
+ d->u.ubuf.maybeSize = size;
+ d->u.ubuf.hasDynamicOffset = true;
+ return b;
+}
+
+/*!
+ \return a shader resource binding for the given binding number, pipeline
+ stages, texture, and sampler specified by \a binding, \a stage, \a tex,
+ \a sampler.
+ */
+QRhiShaderResourceBinding QRhiShaderResourceBinding::sampledTexture(
+ int binding, StageFlags stage, QRhiTexture *tex, QRhiSampler *sampler)
+{
+ QRhiShaderResourceBinding b;
+ QRhiShaderResourceBindingPrivate *d = QRhiShaderResourceBindingPrivate::get(&b);
+ Q_ASSERT(d->ref.load() == 1);
+ d->binding = binding;
+ d->stage = stage;
+ d->type = SampledTexture;
+ d->u.stex.tex = tex;
+ d->u.stex.sampler = sampler;
+ return b;
+}
+
+/*!
+ \return \c true if the contents of the two QRhiShaderResourceBinding
+ objects \a a and \a b are equal. This includes the resources (buffer,
+ texture) and related parameters (offset, size) as well. To only compare
+ layouts (binding point, pipeline stage, resource type), use
+ \l{QRhiShaderResourceBinding::isLayoutCompatible()}{isLayoutCompatible()}
+ instead.
+
+ \relates QRhiShaderResourceBinding
+ */
+bool operator==(const QRhiShaderResourceBinding &a, const QRhiShaderResourceBinding &b) Q_DECL_NOTHROW
+{
+ if (a.d == b.d)
+ return true;
+
+ if (a.d->binding != b.d->binding
+ || a.d->stage != b.d->stage
+ || a.d->type != b.d->type)
+ {
+ return false;
+ }
+
+ switch (a.d->type) {
+ case QRhiShaderResourceBinding::UniformBuffer:
+ if (a.d->u.ubuf.buf != b.d->u.ubuf.buf
+ || a.d->u.ubuf.offset != b.d->u.ubuf.offset
+ || a.d->u.ubuf.maybeSize != b.d->u.ubuf.maybeSize)
+ {
+ return false;
+ }
+ break;
+ case QRhiShaderResourceBinding::SampledTexture:
+ if (a.d->u.stex.tex != b.d->u.stex.tex
+ || a.d->u.stex.sampler != b.d->u.stex.sampler)
+ {
+ return false;
+ }
+ break;
+ default:
+ Q_UNREACHABLE();
+ return false;
+ }
+
+ return true;
+}
+
+/*!
+ \return \c false if all the bindings in the two QRhiShaderResourceBinding
+ objects \a a and \a b are equal; otherwise returns \c true.
+
+ \relates QRhiShaderResourceBinding
+ */
+bool operator!=(const QRhiShaderResourceBinding &a, const QRhiShaderResourceBinding &b) Q_DECL_NOTHROW
+{
+ return !(a == b);
+}
+
+/*!
+ \return the hash value for \a b, using \a seed to seed the calculation.
+
+ \relates QRhiShaderResourceBinding
+ */
+uint qHash(const QRhiShaderResourceBinding &b, uint seed) Q_DECL_NOTHROW
+{
+ const char *u = reinterpret_cast<const char *>(&b.d->u);
+ return seed + b.d->binding + 10 * b.d->stage + 100 * b.d->type
+ + qHash(QByteArray::fromRawData(u, sizeof(b.d->u)), seed);
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug dbg, const QRhiShaderResourceBinding &b)
+{
+ const QRhiShaderResourceBindingPrivate *d = b.d;
+ QDebugStateSaver saver(dbg);
+ dbg.nospace() << "QRhiShaderResourceBinding("
+ << "binding=" << d->binding
+ << " stage=" << d->stage
+ << " type=" << d->type;
+ switch (d->type) {
+ case QRhiShaderResourceBinding::UniformBuffer:
+ dbg.nospace() << " UniformBuffer("
+ << "buffer=" << d->u.ubuf.buf
+ << " offset=" << d->u.ubuf.offset
+ << " maybeSize=" << d->u.ubuf.maybeSize
+ << ')';
+ break;
+ case QRhiShaderResourceBinding::SampledTexture:
+ dbg.nospace() << " SampledTexture("
+ << "texture=" << d->u.stex.tex
+ << " sampler=" << d->u.stex.sampler
+ << ')';
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ dbg.nospace() << ')';
+ return dbg;
+}
+#endif
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug dbg, const QRhiShaderResourceBindings &srb)
+{
+ QDebugStateSaver saver(dbg);
+ dbg.nospace() << "QRhiShaderResourceBindings("
+ << srb.m_bindings
+ << ')';
+ return dbg;
+}
+#endif
+
+/*!
+ \class QRhiGraphicsPipeline
+ \inmodule QtRhi
+ \brief Graphics pipeline state resource.
+
+ \note Setting the shader resource bindings is mandatory. The referenced
+ QRhiShaderResourceBindings must already be built by the time build() is
+ called.
+
+ \note Setting the render pass descriptor is mandatory. To obtain a
+ QRhiRenderPassDescriptor that can be passed to setRenderPassDescriptor(),
+ use either QRhiTextureRenderTarget::newCompatibleRenderPassDescriptor() or
+ QRhiSwapChain::newCompatibleRenderPassDescriptor().
+
+ \note Setting the vertex input layout is mandatory.
+
+ \note Setting the shader stages is mandatory.
+
+ \note sampleCount() defaults to 1 and must match the sample count of the
+ render target's color and depth stencil attachments.
+
+ \note The depth test, depth write, and stencil test are disabled by
+ default.
+
+ \note stencilReadMask() and stencilWriteMask() apply to both faces. They
+ both default to 0xFF.
+ */
+
+/*!
+ \fn void QRhiGraphicsPipeline::setTargetBlends(const QVector<TargetBlend> &blends)
+
+ Sets the blend specification for color attachments. Each element in \a
+ blends corresponds to a color attachment of the render target.
+
+ By default no blends are set, which is a shortcut to disabling blending and
+ enabling color write for all four channels.
+ */
+
+/*!
+ \enum QRhiGraphicsPipeline::Flag
+
+ Flag values for describing the dynamic state of the pipeline. The viewport is always dynamic.
+
+ \value UsesBlendConstants Indicates that a blend color constant will be set
+ via QRhiCommandBuffer::setBlendConstants()
+
+ \value UsesStencilRef Indicates that a stencil reference value will be set
+ via QRhiCommandBuffer::setStencilRef()
+
+ \value UsesScissor Indicates that a scissor rectangle will be set via
+ QRhiCommandBuffer::setScissor()
+ */
+
+/*!
+ \enum QRhiGraphicsPipeline::Topology
+ Specifies the primitive topology
+
+ \value Triangles (default)
+ \value TriangleStrip
+ \value Lines
+ \value LineStrip
+ \value Points
+ */
+
+/*!
+ \enum QRhiGraphicsPipeline::CullMode
+ Specifies the culling mode
+
+ \value None No culling (default)
+ \value Front Cull front faces
+ \value Back Cull back faces
+ */
+
+/*!
+ \enum QRhiGraphicsPipeline::FrontFace
+ Specifies the front face winding order
+
+ \value CCW Counter clockwise (default)
+ \value CW Clockwise
+ */
+
+/*!
+ \enum QRhiGraphicsPipeline::ColorMaskComponent
+ Flag values for specifying the color write mask
+
+ \value R
+ \value G
+ \value B
+ \value A
+ */
+
+/*!
+ \enum QRhiGraphicsPipeline::BlendFactor
+ Specifies the blend factor
+
+ \value Zero
+ \value One
+ \value SrcColor
+ \value OneMinusSrcColor
+ \value DstColor
+ \value OneMinusDstColor
+ \value SrcAlpha
+ \value OneMinusSrcAlpha
+ \value DstAlpha
+ \value OneMinusDstAlpha
+ \value ConstantColor
+ \value OneMinusConstantColor
+ \value ConstantAlpha
+ \value OneMinusConstantAlpha
+ \value SrcAlphaSaturate
+ \value Src1Color
+ \value OneMinusSrc1Color
+ \value Src1Alpha
+ \value OneMinusSrc1Alpha
+ */
+
+/*!
+ \enum QRhiGraphicsPipeline::BlendOp
+ Specifies the blend operation
+
+ \value Add
+ \value Subtract
+ \value ReverseSubtract
+ \value Min
+ \value Max
+ */
+
+/*!
+ \enum QRhiGraphicsPipeline::CompareOp
+ Specifies the depth or stencil comparison function
+
+ \value Never
+ \value Less (default for depth)
+ \value Equal
+ \value LessOrEqual
+ \value Greater
+ \value NotEqual
+ \value GreaterOrEqual
+ \value Always (default for stencil)
+ */
+
+/*!
+ \enum QRhiGraphicsPipeline::StencilOp
+ Specifies the stencil operation
+
+ \value StencilZero
+ \value Keep (default)
+ \value Replace
+ \value IncrementAndClamp
+ \value DecrementAndClamp
+ \value Invert
+ \value IncrementAndWrap
+ \value DecrementAndWrap
+ */
+
+/*!
+ \class QRhiGraphicsPipeline::TargetBlend
+ \inmodule QtRhi
+ \brief Describes the blend state for one color attachment.
+
+ Defaults to color write enabled, blending disabled. The blend values are
+ set up for pre-multiplied alpha (One, OneMinusSrcAlpha, One,
+ OneMinusSrcAlpha) by default.
+ */
+
+/*!
+ \class QRhiGraphicsPipeline::StencilOpState
+ \inmodule QtRhi
+ \brief Describes the stencil operation state.
+ */
+
+/*!
+ \internal
+ */
+QRhiGraphicsPipeline::QRhiGraphicsPipeline(QRhiImplementation *rhi)
+ : QRhiResource(rhi)
+{
+}
+
+/*!
+ \return the resource type.
+ */
+QRhiResource::Type QRhiGraphicsPipeline::resourceType() const
+{
+ return GraphicsPipeline;
+}
+
+/*!
+ \fn bool QRhiGraphicsPipeline::build()
+
+ Creates the corresponding native graphics resources. If there are already
+ resources present due to an earlier build() with no corresponding
+ release(), then release() is called implicitly first.
+
+ \return \c true when successful, \c false when a graphics operation failed.
+ Regardless of the return value, calling release() is always safe.
+ */
+
+/*!
+ \fn void QRhiGraphicsPipeline::setDepthTest(bool enable)
+
+ Enables or disables depth testing. Both depth test and the writing out of
+ depth data are disabled by default.
+
+ \sa setDepthWrite()
+ */
+
+/*!
+ \fn void QRhiGraphicsPipeline::setDepthWrite(bool enable)
+
+ Controls the writing out of depth data into the depth buffer. By default
+ this is disabled. Depth write is typically enabled together with the depth
+ test.
+
+ \note Enabling depth write without having depth testing enabled may not
+ lead to the desired result, and should be avoided.
+
+ \sa setDepthTest()
+ */
+
+/*!
+ \class QRhiSwapChain
+ \inmodule QtRhi
+ \brief Swapchain resource.
+
+ A swapchain enables presenting rendering results to a surface. A swapchain
+ is typically backed by a set of color buffers. Of these, one is displayed
+ at a time.
+
+ Below is a typical pattern for creating and managing a swapchain and some
+ associated resources in order to render onto a QWindow:
+
+ \badcode
+ void init()
+ {
+ sc = rhi->newSwapChain();
+ ds = rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil,
+ QSize(), // no need to set the size yet
+ 1,
+ QRhiRenderBuffer::UsedWithSwapChainOnly);
+ sc->setWindow(window);
+ sc->setDepthStencil(ds);
+ rp = sc->newCompatibleRenderPassDescriptor();
+ sc->setRenderPassDescriptor(rp);
+ resizeSwapChain();
+ }
+
+ void resizeSwapChain()
+ {
+ const QSize outputSize = sc->surfacePixelSize();
+ ds->setPixelSize(outputSize);
+ ds->build();
+ hasSwapChain = sc->buildOrResize();
+ }
+
+ void render()
+ {
+ if (!hasSwapChain || notExposed)
+ return;
+
+ if (sc->currentPixelSize() != sc->surfacePixelSize() || newlyExposed) {
+ resizeSwapChain();
+ if (!hasSwapChain)
+ return;
+ newlyExposed = false;
+ }
+
+ rhi->beginFrame(sc);
+ // ...
+ rhi->endFrame(sc);
+ }
+ \endcode
+
+ Avoid relying on QWindow resize events to resize swapchains, especially
+ considering that surface sizes may not always fully match the QWindow
+ reported dimensions. The safe, cross-platform approach is to do the check
+ via surfacePixelSize() whenever starting a new frame.
+
+ Releasing the swapchain must happen while the QWindow and the underlying
+ native window is fully up and running. Building on the previous example:
+
+ \badcode
+ void releaseSwapChain()
+ {
+ if (hasSwapChain) {
+ sc->release();
+ hasSwapChain = false;
+ }
+ }
+
+ // assuming Window is our QWindow subclass
+ bool Window::event(QEvent *e)
+ {
+ switch (e->type()) {
+ case QEvent::UpdateRequest: // for QWindow::requestUpdate()
+ render();
+ break;
+ case QEvent::PlatformSurface:
+ if (static_cast<QPlatformSurfaceEvent *>(e)->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed)
+ releaseSwapChain();
+ break;
+ default:
+ break;
+ }
+ return QWindow::event(e);
+ }
+ \endcode
+
+ Initializing the swapchain and starting to render the first frame cannot
+ start at any time. The safe, cross-platform approach is to rely on expose
+ events. QExposeEvent is a loosely specified event that is sent whenever a
+ window gets mapped, obscured, and resized, depending on the platform.
+
+ \badcode
+ void Window::exposeEvent(QExposeEvent *)
+ {
+ // initialize and start rendering when the window becomes usable for graphics purposes
+ if (isExposed() && !running) {
+ running = true;
+ init();
+ }
+
+ // stop pushing frames when not exposed or size becomes 0
+ if ((!isExposed() || (hasSwapChain && sc->surfacePixelSize().isEmpty())) && running)
+ notExposed = true;
+
+ // continue when exposed again and the surface has a valid size
+ if (isExposed() && running && notExposed && !sc->surfacePixelSize().isEmpty()) {
+ notExposed = false;
+ newlyExposed = true;
+ }
+
+ if (isExposed() && !sc->surfacePixelSize().isEmpty())
+ render();
+ }
+ \endcode
+
+ Once the rendering has started, a simple way to request a new frame is
+ QWindow::requestUpdate(). While on some platforms this is merely a small
+ timer, on others it has a specific implementation: for instance on macOS or
+ iOS it may be backed by
+ \l{https://developer.apple.com/documentation/corevideo/cvdisplaylink?language=objc}{CVDisplayLink}.
+ The example above is already prepared for update requests by handling
+ QEvent::UpdateRequest.
+
+ While acting as a QRhiRenderTarget, QRhiSwapChain also manages a
+ QRhiCommandBuffer. Calling QRhi::endFrame() submits the recorded commands
+ and also enqueues a \c present request. The default behavior is to do this
+ with a swap interval of 1, meaning synchronizing to the display's vertical
+ refresh is enabled. Thus the rendering thread calling beginFrame() and
+ endFrame() will get throttled to vsync. On some backends this can be
+ disabled by passing QRhiSwapChain:NoVSync in flags().
+
+ Multisampling (MSAA) is handled transparently to the applications when
+ requested via setSampleCount(). Where applicable, QRhiSwapChain will take
+ care of creating additional color buffers and issuing a multisample resolve
+ command at the end of a frame. For OpenGL, it is necessary to request the
+ appropriate sample count also via QSurfaceFormat, by calling
+ QSurfaceFormat::setDefaultFormat() before initializing the QRhi.
+ */
+
+/*!
+ \enum QRhiSwapChain::Flag
+ Flag values to describe swapchain properties
+
+ \value SurfaceHasPreMulAlpha Indicates that the target surface has
+ transparency with premultiplied alpha.
+
+ \value SurfaceHasNonPreMulAlpha Indicates the target surface has
+ transparencyt with non-premultiplied alpha.
+
+ \value sRGB Requests to pick an sRGB format for the swapchain and/or its
+ render target views, where applicable. Note that this implies that sRGB
+ framebuffer update and blending will get enabled for all content targeting
+ this swapchain, and opting out is not possible. For OpenGL, set
+ \l{QSurfaceFormat::sRGBColorSpace}{sRGBColorSpace} on the QSurfaceFormat of
+ the QWindow in addition.
+
+ \value UsedAsTransferSource Indicates the the swapchain will be used as the
+ source of a readback in QRhiResourceUpdateBatch::readBackTexture().
+
+ \value NoVSync Requests disabling waiting for vertical sync, also avoiding
+ throttling the rendering thread. The behavior is backend specific and
+ applicable only where it is possible to control this. Some may ignore the
+ request altogether. For OpenGL, try instead setting the swap interval to 0
+ on the QWindow via QSurfaceFormat::setSwapInterval().
+
+ \value MinimalBufferCount Requests creating the swapchain with the minimum
+ number of buffers, which is in practice 2, unless the graphics
+ implementation has a higher minimum number than that. Only applicable with
+ backends where such control is available via the graphics API, for example,
+ Vulkan. By default it is up to the backend to decide what number of buffers
+ it requests (in practice this is almost always either 2 or 3), and it is
+ not the applications' concern. However, on Vulkan for instance the backend
+ will likely prefer the higher number (3), for example to avoid odd
+ performance issues with some Vulkan implementations on mobile devices. It
+ could be that on some platforms it can prove to be beneficial to force the
+ lower buffer count (2), so this flag allows forcing that. Note that all
+ this has no effect on the number of frames kept in flight, so the CPU
+ (QRhi) will still prepare frames at most \c{N - 1} frames ahead of the GPU,
+ even when the swapchain image buffer count larger than \c N. (\c{N} =
+ QRhi::FramesInFlight and typically 2).
+ */
+
+/*!
+ \internal
+ */
+QRhiSwapChain::QRhiSwapChain(QRhiImplementation *rhi)
+ : QRhiResource(rhi)
+{
+}
+
+/*!
+ \return the resource type.
+ */
+QRhiResource::Type QRhiSwapChain::resourceType() const
+{
+ return SwapChain;
+}
+
+/*!
+ \fn QSize QRhiSwapChain::currentPixelSize() const
+
+ \return the size with which the swapchain was last successfully built. Use
+ this to decide if buildOrResize() needs to be called again: if
+ \c{currentPixelSize() != surfacePixelSize()} then the swapchain needs to be
+ resized.
+
+ \sa surfacePixelSize()
+ */
+
+/*!
+ \fn QSize QRhiSwapChain::surfacePixelSize()
+
+ \return The size of the window's associated surface or layer. Do not assume
+ this is the same as QWindow::size() * QWindow::devicePixelRatio().
+
+ Can be called before buildOrResize() (but with window() already set), which
+ allows setting the correct size for the depth-stencil buffer that is then
+ used together with the swapchain's color buffers. Also used in combination
+ with currentPixelSize() to detect size changes.
+
+ \sa currentPixelSize()
+ */
+
+/*!
+ \fn QRhiCommandBuffer *QRhiSwapChain::currentFrameCommandBuffer()
+
+ \return a command buffer on which rendering commands can be recorded. Only
+ valid within a QRhi::beginFrame() - QRhi::endFrame() block where
+ beginFrame() was called with this swapchain.
+
+ \note the value must not be cached and reused between frames
+*/
+
+/*!
+ \fn QRhiRenderTarget *QRhiSwapChain::currentFrameRenderTarget()
+
+ \return a render target that can used with beginPass() in order to render
+ the the swapchain's current backbuffer. Only valid within a
+ QRhi::beginFrame() - QRhi::endFrame() block where beginFrame() was called
+ with this swapchain.
+
+ \note the value must not be cached and reused between frames
+ */
+
+/*!
+ \fn bool QRhiSwapChain::buildOrResize()
+
+ Creates the swapchain if not already done and resizes the swapchain buffers
+ to match the current size of the targeted surface. Call this whenever the
+ size of the target surface is different than before.
+
+ \note call release() only when the swapchain needs to be released
+ completely, typically upon
+ QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed. To perform resizing, just
+ call buildOrResize().
+
+ \return \c true when successful, \c false when a graphics operation failed.
+ Regardless of the return value, calling release() is always safe.
+ */
+
+/*!
+ \class QRhiCommandBuffer
+ \inmodule QtRhi
+ \brief Command buffer resource.
+
+ Not creatable by applications at the moment. The only ways to obtain a
+ valid QRhiCommandBuffer are to get it from the targeted swapchain via
+ QRhiSwapChain::currentFrameCommandBuffer(), or, in case of rendering
+ completely offscreen, initializing one via QRhi::beginOffscreenFrame().
+ */
+
+/*!
+ \enum QRhiCommandBuffer::IndexFormat
+ Specifies the index data type
+
+ \value IndexUInt16 Unsigned 16-bit (quint16)
+ \value IndexUInt32 Unsigned 32-bit (quint32)
+ */
+
+/*!
+ \typedef QRhiCommandBuffer::DynamicOffset
+
+ Synonym for QPair<int, quint32>. The first entry is the binding, the second
+ is the offset in the buffer.
+*/
+
+/*!
+ \typedef QRhiCommandBuffer::VertexInput
+
+ Synonym for QPair<QRhiBuffer *, quint32>. The second entry is an offset in
+ the buffer specified by the first.
+*/
+
+/*!
+ \internal
+ */
+QRhiCommandBuffer::QRhiCommandBuffer(QRhiImplementation *rhi)
+ : QRhiResource(rhi)
+{
+}
+
+/*!
+ \return the resource type.
+ */
+QRhiResource::Type QRhiCommandBuffer::resourceType() const
+{
+ return CommandBuffer;
+}
+
+QRhiImplementation::~QRhiImplementation()
+{
+ qDeleteAll(resUpdPool);
+
+ // Be nice and show something about leaked stuff. Though we may not get
+ // this far with some backends where the allocator or the api may check
+ // and freak out for unfreed graphics objects in the derived dtor already.
+#ifndef QT_NO_DEBUG
+ if (!resources.isEmpty()) {
+ qWarning("QRhi %p going down with %d unreleased resources. This is not nice.",
+ q, resources.count());
+ for (QRhiResource *res : qAsConst(resources)) {
+ qWarning(" Resource %p (%s)", res, res->m_objectName.constData());
+ res->m_rhi = nullptr;
+ }
+ }
+#endif
+}
+
+bool QRhiImplementation::isCompressedFormat(QRhiTexture::Format format) const
+{
+ return (format >= QRhiTexture::BC1 && format <= QRhiTexture::BC7)
+ || (format >= QRhiTexture::ETC2_RGB8 && format <= QRhiTexture::ETC2_RGBA8)
+ || (format >= QRhiTexture::ASTC_4x4 && format <= QRhiTexture::ASTC_12x12);
+}
+
+void QRhiImplementation::compressedFormatInfo(QRhiTexture::Format format, const QSize &size,
+ quint32 *bpl, quint32 *byteSize,
+ QSize *blockDim) const
+{
+ int xdim = 4;
+ int ydim = 4;
+ quint32 blockSize = 0;
+
+ switch (format) {
+ case QRhiTexture::BC1:
+ blockSize = 8;
+ break;
+ case QRhiTexture::BC2:
+ blockSize = 16;
+ break;
+ case QRhiTexture::BC3:
+ blockSize = 16;
+ break;
+ case QRhiTexture::BC4:
+ blockSize = 8;
+ break;
+ case QRhiTexture::BC5:
+ blockSize = 16;
+ break;
+ case QRhiTexture::BC6H:
+ blockSize = 16;
+ break;
+ case QRhiTexture::BC7:
+ blockSize = 16;
+ break;
+
+ case QRhiTexture::ETC2_RGB8:
+ blockSize = 8;
+ break;
+ case QRhiTexture::ETC2_RGB8A1:
+ blockSize = 8;
+ break;
+ case QRhiTexture::ETC2_RGBA8:
+ blockSize = 16;
+ break;
+
+ case QRhiTexture::ASTC_4x4:
+ blockSize = 16;
+ break;
+ case QRhiTexture::ASTC_5x4:
+ blockSize = 16;
+ xdim = 5;
+ break;
+ case QRhiTexture::ASTC_5x5:
+ blockSize = 16;
+ xdim = ydim = 5;
+ break;
+ case QRhiTexture::ASTC_6x5:
+ blockSize = 16;
+ xdim = 6;
+ ydim = 5;
+ break;
+ case QRhiTexture::ASTC_6x6:
+ blockSize = 16;
+ xdim = ydim = 6;
+ break;
+ case QRhiTexture::ASTC_8x5:
+ blockSize = 16;
+ xdim = 8;
+ ydim = 5;
+ break;
+ case QRhiTexture::ASTC_8x6:
+ blockSize = 16;
+ xdim = 8;
+ ydim = 6;
+ break;
+ case QRhiTexture::ASTC_8x8:
+ blockSize = 16;
+ xdim = ydim = 8;
+ break;
+ case QRhiTexture::ASTC_10x5:
+ blockSize = 16;
+ xdim = 10;
+ ydim = 5;
+ break;
+ case QRhiTexture::ASTC_10x6:
+ blockSize = 16;
+ xdim = 10;
+ ydim = 6;
+ break;
+ case QRhiTexture::ASTC_10x8:
+ blockSize = 16;
+ xdim = 10;
+ ydim = 8;
+ break;
+ case QRhiTexture::ASTC_10x10:
+ blockSize = 16;
+ xdim = ydim = 10;
+ break;
+ case QRhiTexture::ASTC_12x10:
+ blockSize = 16;
+ xdim = 12;
+ ydim = 10;
+ break;
+ case QRhiTexture::ASTC_12x12:
+ blockSize = 16;
+ xdim = ydim = 12;
+ break;
+
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+
+ const quint32 wblocks = (size.width() + xdim - 1) / xdim;
+ const quint32 hblocks = (size.height() + ydim - 1) / ydim;
+
+ if (bpl)
+ *bpl = wblocks * blockSize;
+ if (byteSize)
+ *byteSize = wblocks * hblocks * blockSize;
+ if (blockDim)
+ *blockDim = QSize(xdim, ydim);
+}
+
+void QRhiImplementation::textureFormatInfo(QRhiTexture::Format format, const QSize &size,
+ quint32 *bpl, quint32 *byteSize) const
+{
+ if (isCompressedFormat(format)) {
+ compressedFormatInfo(format, size, bpl, byteSize, nullptr);
+ return;
+ }
+
+ quint32 bpc = 0;
+ switch (format) {
+ case QRhiTexture::RGBA8:
+ bpc = 4;
+ break;
+ case QRhiTexture::BGRA8:
+ bpc = 4;
+ break;
+ case QRhiTexture::R8:
+ bpc = 1;
+ break;
+ case QRhiTexture::R16:
+ bpc = 2;
+ break;
+ case QRhiTexture::RED_OR_ALPHA8:
+ bpc = 1;
+ break;
+
+ case QRhiTexture::RGBA16F:
+ bpc = 8;
+ break;
+ case QRhiTexture::RGBA32F:
+ bpc = 16;
+ break;
+
+ case QRhiTexture::D16:
+ bpc = 2;
+ break;
+ case QRhiTexture::D32F:
+ bpc = 4;
+ break;
+
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+
+ if (bpl)
+ *bpl = size.width() * bpc;
+ if (byteSize)
+ *byteSize = size.width() * size.height() * bpc;
+}
+
+// Approximate because it excludes subresource alignment or multisampling.
+quint32 QRhiImplementation::approxByteSizeForTexture(QRhiTexture::Format format, const QSize &baseSize,
+ int mipCount, int layerCount)
+{
+ quint32 approxSize = 0;
+ for (int level = 0; level < mipCount; ++level) {
+ quint32 byteSize = 0;
+ const QSize size(qFloor(float(qMax(1, baseSize.width() >> level))),
+ qFloor(float(qMax(1, baseSize.height() >> level))));
+ textureFormatInfo(format, size, nullptr, &byteSize);
+ approxSize += byteSize;
+ }
+ approxSize *= layerCount;
+ return approxSize;
+}
+
+/*!
+ \internal
+ */
+QRhi::QRhi()
+{
+}
+
+/*!
+ Destructor. Destroys the backend and releases resources.
+ */
+QRhi::~QRhi()
+{
+ if (!d)
+ return;
+
+ qDeleteAll(d->pendingReleaseAndDestroyResources);
+ d->pendingReleaseAndDestroyResources.clear();
+
+ runCleanup();
+
+ d->destroy();
+ delete d;
+}
+
+/*!
+ \return a new QRhi instance with a backend for the graphics API specified by \a impl.
+
+ \a params must point to an instance of one of the backend-specific
+ subclasses of QRhiInitParams, such as, QRhiVulkanInitParams,
+ QRhiMetalInitParams, QRhiD3D11InitParams, QRhiGles2InitParams. See these
+ classes for examples on creating a QRhi.
+
+ \a flags is optional. It is used to enable profile and debug related
+ features that are potentially expensive and should only be used during
+ development.
+ */
+QRhi *QRhi::create(Implementation impl, QRhiInitParams *params, Flags flags, QRhiNativeHandles *importDevice)
+{
+ QScopedPointer<QRhi> r(new QRhi);
+
+ switch (impl) {
+ case Null:
+ r->d = new QRhiNull(static_cast<QRhiNullInitParams *>(params));
+ break;
+ case Vulkan:
+#if QT_CONFIG(vulkan)
+ r->d = new QRhiVulkan(static_cast<QRhiVulkanInitParams *>(params),
+ static_cast<QRhiVulkanNativeHandles *>(importDevice));
+ break;
+#else
+ qWarning("This build of Qt has no Vulkan support");
+ break;
+#endif
+ case OpenGLES2:
+#ifndef QT_NO_OPENGL
+ r->d = new QRhiGles2(static_cast<QRhiGles2InitParams *>(params),
+ static_cast<QRhiGles2NativeHandles *>(importDevice));
+ break;
+#else
+ qWarning("This build of Qt has no OpenGL support");
+ break;
+#endif
+ case D3D11:
+#ifdef Q_OS_WIN
+ r->d = new QRhiD3D11(static_cast<QRhiD3D11InitParams *>(params),
+ static_cast<QRhiD3D11NativeHandles *>(importDevice));
+ break;
+#else
+ qWarning("This platform has no Direct3D 11 support");
+ break;
+#endif
+ case Metal:
+//#ifdef Q_OS_DARWIN
+#ifdef Q_OS_MACOS
+ r->d = new QRhiMetal(static_cast<QRhiMetalInitParams *>(params),
+ static_cast<QRhiMetalNativeHandles *>(importDevice));
+ break;
+#else
+ qWarning("This platform has no Metal support");
+ break;
+#endif
+ default:
+ break;
+ }
+
+ if (r->d) {
+ r->d->q = r.data();
+ if (flags.testFlag(EnableProfiling)) {
+ QRhiProfilerPrivate *profD = QRhiProfilerPrivate::get(&r->d->profiler);
+ profD->rhiDWhenEnabled = r->d;
+ }
+ r->d->debugMarkers = flags.testFlag(EnableDebugMarkers);
+ if (r->d->create(flags)) {
+ r->d->implType = impl;
+ r->d->implThread = QThread::currentThread();
+ return r.take();
+ }
+ }
+
+ return nullptr;
+}
+
+/*!
+ \return the backend type for this QRhi.
+ */
+QRhi::Implementation QRhi::backend() const
+{
+ return d->implType;
+}
+
+/*!
+ \return the thread on which the QRhi was \l{QRhi::create()}{initialized}.
+ */
+QThread *QRhi::thread() const
+{
+ return d->implThread;
+}
+
+/*!
+ Registers a \a callback that is invoked either when the QRhi is destroyed,
+ or when runCleanup() is called.
+
+ The callback will run with the graphics resource still available, so this
+ provides an opportunity for the application to cleanly release QRhiResource
+ instances belonging to the QRhi. This is particularly useful for managing
+ the lifetime of resources stored in \c cache type of objects, where the
+ cache holds QRhiResources or objects containing QRhiResources.
+
+ \sa runCleanup(), ~QRhi()
+ */
+void QRhi::addCleanupCallback(const CleanupCallback &callback)
+{
+ d->addCleanupCallback(callback);
+}
+
+/*!
+ Invokes all registered cleanup functions. The list of cleanup callbacks it
+ then cleared. Normally destroying the QRhi does this automatically, but
+ sometimes it can be useful to trigger cleanup in order to release all
+ cached, non-essential resources.
+
+ \sa addCleanupCallback()
+ */
+void QRhi::runCleanup()
+{
+ for (const CleanupCallback &f : qAsConst(d->cleanupCallbacks))
+ f(this);
+
+ d->cleanupCallbacks.clear();
+}
+
+/*!
+ \class QRhiResourceUpdateBatch
+ \inmodule QtRhi
+ \brief Records upload and copy type of operations.
+
+ With QRhi it is no longer possible to perform copy type of operations at
+ arbitrary times. Instead, all such operations are recorded into batches
+ that are then passed, most commonly, to QRhiCommandBuffer::beginPass().
+ What then happens under the hood is hidden from the application: the
+ underlying implementations can defer and implement these operations in
+ various different ways.
+
+ A resource update batch owns no graphics resources and does not perform any
+ actual operations on its own. It should rather be viewed as a command
+ buffer for update, upload, and copy type of commands.
+
+ To get an available, empty batch from the pool, call
+ QRhi::nextResourceUpdateBatch().
+ */
+
+/*!
+ \internal
+ */
+QRhiResourceUpdateBatch::QRhiResourceUpdateBatch(QRhiImplementation *rhi)
+ : d(new QRhiResourceUpdateBatchPrivate)
+{
+ d->q = this;
+ d->rhi = rhi;
+}
+
+QRhiResourceUpdateBatch::~QRhiResourceUpdateBatch()
+{
+ delete d;
+}
+
+/*!
+ \return the batch to the pool. This should only be used when the batch is
+ not passed to one of QRhiCommandBuffer::beginPass(),
+ QRhiCommandBuffer::endPass(), or QRhiCommandBuffer::resourceUpdate()
+ because these implicitly call release().
+
+ \note QRhiResourceUpdateBatch instances must never by \c deleted by
+ applications.
+ */
+void QRhiResourceUpdateBatch::release()
+{
+ d->free();
+}
+
+/*!
+ Copies all queued operations from the \a other batch into this one.
+
+ \note \a other is not changed in any way, typically it will still need a
+ release()
+
+ This allows for a convenient pattern where resource updates that are
+ already known during the initialization step are collected into a batch
+ that is then merged into another when starting to first render pass later
+ on:
+
+ \badcode
+ void init()
+ {
+ ...
+ initialUpdates = rhi->nextResourceUpdateBatch();
+ initialUpdates->uploadStaticBuffer(vbuf, vertexData);
+ initialUpdates->uploadStaticBuffer(ibuf, indexData);
+ ...
+ }
+
+ void render()
+ {
+ ...
+ QRhiResourceUpdateBatch *resUpdates = rhi->nextResourceUpdateBatch();
+ if (initialUpdates) {
+ resUpdates->merge(initialUpdates);
+ initialUpdates->release();
+ initialUpdates = nullptr;
+ }
+ resUpdates->updateDynamicBuffer(...);
+ ...
+ cb->beginPass(rt, clearCol, clearDs, resUpdates);
+ }
+ \endcode
+ */
+void QRhiResourceUpdateBatch::merge(QRhiResourceUpdateBatch *other)
+{
+ d->merge(other->d);
+}
+
+/*!
+ Enqueues updating a region of a QRhiBuffer \a buf created with the type
+ QRhiBuffer::Dynamic.
+
+ The region is specified \a offset and \a size. The actual bytes to write
+ are specified by \a data which must have at least \a size bytes available.
+ \a data can safely be destroyed or changed once this function returns.
+
+ \note If host writes are involved, which is the case with
+ updateDynamicBuffer() typically as such buffers are backed by host visible
+ memory with most backends, they may accumulate within a frame. Thus pass 1
+ reading a region changed by a batch passed to pass 2 may see the changes
+ specified in pass 2's update batch.
+
+ \note QRhi transparently manages double buffering in order to prevent
+ stalling the graphics pipeline. The fact that a QRhiBuffer may have
+ multiple native underneath can be safely ignored when using the QRhi and
+ QRhiResourceUpdateBatch.
+ */
+void QRhiResourceUpdateBatch::updateDynamicBuffer(QRhiBuffer *buf, int offset, int size, const void *data)
+{
+ if (size > 0)
+ d->dynamicBufferUpdates.append({ buf, offset, size, data });
+}
+
+/*!
+ Enqueues updating a region of a QRhiBuffer \a buf created with the type
+ QRhiBuffer::Immutable or QRhiBuffer::Static.
+
+ The region is specified \a offset and \a size. The actual bytes to write
+ are specified by \a data which must have at least \a size bytes available.
+ \a data can safely be destroyed or changed once this function returns.
+ */
+void QRhiResourceUpdateBatch::uploadStaticBuffer(QRhiBuffer *buf, int offset, int size, const void *data)
+{
+ if (size > 0)
+ d->staticBufferUploads.append({ buf, offset, size, data });
+}
+
+/*!
+ Enqueues updating the entire QRhiBuffer \a buf created with the type
+ QRhiBuffer::Immutable or QRhiBuffer::Static.
+ */
+void QRhiResourceUpdateBatch::uploadStaticBuffer(QRhiBuffer *buf, const void *data)
+{
+ if (buf->size() > 0)
+ d->staticBufferUploads.append({ buf, 0, 0, data });
+}
+
+/*!
+ Enqueues uploading the image data for one or more mip levels in one or more
+ layers of the texture \a tex.
+
+ The details of the copy (source QImage or compressed texture data, regions,
+ target layers and levels) are described in \a desc.
+ */
+void QRhiResourceUpdateBatch::uploadTexture(QRhiTexture *tex, const QRhiTextureUploadDescription &desc)
+{
+ if (!desc.entries().isEmpty())
+ d->textureOps.append(QRhiResourceUpdateBatchPrivate::TextureOp::textureUpload(tex, desc));
+}
+
+/*!
+ Enqueues uploading the image data for mip level 0 of layer 0 of the texture
+ \a tex.
+
+ \a tex must have an uncompressed format. Its format must also be compatible
+ with the QImage::format() of \a image. The source data is given in \a
+ image.
+ */
+void QRhiResourceUpdateBatch::uploadTexture(QRhiTexture *tex, const QImage &image)
+{
+ uploadTexture(tex, QRhiTextureUploadEntry(0, 0, image));
+}
+
+/*!
+ Enqueues a texture-to-texture copy operation from \a src into \a dst as
+ described by \a desc.
+
+ \note The source texture \a src must be created with
+ QRhiTexture::UsedAsTransferSource.
+ */
+void QRhiResourceUpdateBatch::copyTexture(QRhiTexture *dst, QRhiTexture *src, const QRhiTextureCopyDescription &desc)
+{
+ d->textureOps.append(QRhiResourceUpdateBatchPrivate::TextureOp::textureCopy(dst, src, desc));
+}
+
+/*!
+ Enqueues a texture-to-host copy operation as described by \a rb.
+
+ Normally \a rb will specify a QRhiTexture as the source. However, when the
+ swapchain in the current frame was created with
+ QRhiSwapChain::UsedAsTransferSource, it can also be the source of the
+ readback. For this, leave the texture set to null in \a rb.
+
+ Unlike other operations, the results here need to be processed by the
+ application. Therefore, \a result provides not just the data but also a
+ callback as operations on the batch are asynchronous by nature:
+
+ \badcode
+ beginFrame(sc);
+ beginPass
+ ...
+ QRhiReadbackResult *rbResult = new QRhiReadbackResult;
+ rbResult->completed = [rbResult] {
+ {
+ const QImage::Format fmt = QImage::Format_RGBA8888_Premultiplied; // fits QRhiTexture::RGBA8
+ const uchar *p = reinterpret_cast<const uchar *>(rbResult->data.constData());
+ QImage image(p, rbResult->pixelSize.width(), rbResult->pixelSize.height(), fmt);
+ image.save("result.png");
+ }
+ delete rbResult;
+ };
+ u = nextResourceUpdateBatch();
+ QRhiReadbackDescription rb; // no texture -> uses the current backbuffer of sc
+ u->readBackTexture(rb, rbResult);
+ endPass(u);
+ endFrame(sc);
+ \endcode
+
+ \note The texture must be created with QRhiTexture::UsedAsTransferSource.
+
+ \note Multisample textures cannot be read back.
+
+ \note The readback returns raw byte data, in order to allow the applications
+ to interpret it in any way they see fit. Be aware of the blending settings
+ of rendering code: if the blending is set up to rely on premultiplied alpha,
+ the results of the readback must also be interpreted as Premultiplied.
+
+ \note When interpreting the resulting raw data, be aware that the readback
+ happens with a byte ordered format. A \l{QRhiTexture::RGBA8}{RGBA8} texture
+ maps therefore to byte ordered QImage formats, such as,
+ QImage::Format_RGBA8888.
+ */
+void QRhiResourceUpdateBatch::readBackTexture(const QRhiReadbackDescription &rb, QRhiReadbackResult *result)
+{
+ d->textureOps.append(QRhiResourceUpdateBatchPrivate::TextureOp::textureRead(rb, result));
+}
+
+/*!
+ Enqueues a mipmap generation operation for the specified \a layer of texture
+ \a tex.
+
+ \note The texture must be created with QRhiTexture::MipMapped and
+ QRhiTexture::UsedWithGenerateMips.
+ */
+void QRhiResourceUpdateBatch::generateMips(QRhiTexture *tex, int layer)
+{
+ d->textureOps.append(QRhiResourceUpdateBatchPrivate::TextureOp::textureMipGen(tex, layer));
+}
+
+/*!
+ \return an available, empty batch to which copy type of operations can be
+ recorded.
+
+ \note the return value is not owned by the caller and must never be
+ destroyed. Instead, the batch is returned the the pool for reuse by passing
+ it to QRhiCommandBuffer::beginPass(), QRhiCommandBuffer::endPass(), or
+ QRhiCommandBuffer::resourceUpdate(), or by calling
+ QRhiResourceUpdateBatch::release() on it.
+
+ \note Can be called outside beginFrame() - endFrame() as well since a batch
+ instance just collects data on its own, it does not perform any operations.
+ */
+QRhiResourceUpdateBatch *QRhi::nextResourceUpdateBatch()
+{
+ auto nextFreeBatch = [this]() -> QRhiResourceUpdateBatch * {
+ for (int i = 0, ie = d->resUpdPoolMap.count(); i != ie; ++i) {
+ if (!d->resUpdPoolMap.testBit(i)) {
+ d->resUpdPoolMap.setBit(i);
+ QRhiResourceUpdateBatch *u = d->resUpdPool[i];
+ QRhiResourceUpdateBatchPrivate::get(u)->poolIndex = i;
+ return u;
+ }
+ }
+ return nullptr;
+ };
+
+ QRhiResourceUpdateBatch *u = nextFreeBatch();
+ if (!u) {
+ const int oldSize = d->resUpdPool.count();
+ const int newSize = oldSize + 4;
+ d->resUpdPool.resize(newSize);
+ d->resUpdPoolMap.resize(newSize);
+ for (int i = oldSize; i < newSize; ++i)
+ d->resUpdPool[i] = new QRhiResourceUpdateBatch(d);
+ u = nextFreeBatch();
+ Q_ASSERT(u);
+ }
+
+ return u;
+}
+
+void QRhiResourceUpdateBatchPrivate::free()
+{
+ Q_ASSERT(poolIndex >= 0 && rhi->resUpdPool[poolIndex] == q);
+
+ dynamicBufferUpdates.clear();
+ staticBufferUploads.clear();
+ textureOps.clear();
+
+ rhi->resUpdPoolMap.clearBit(poolIndex);
+ poolIndex = -1;
+}
+
+void QRhiResourceUpdateBatchPrivate::merge(QRhiResourceUpdateBatchPrivate *other)
+{
+ dynamicBufferUpdates += other->dynamicBufferUpdates;
+ staticBufferUploads += other->staticBufferUploads;
+ textureOps += other->textureOps;
+}
+
+/*!
+ Sometimes committing resource updates is necessary without starting a
+ render pass. Not often needed, updates should typically be passed to
+ beginPass (or endPass, in case of readbacks) instead.
+
+ \note Cannot be called inside a pass.
+ */
+void QRhiCommandBuffer::resourceUpdate(QRhiResourceUpdateBatch *resourceUpdates)
+{
+ if (resourceUpdates)
+ m_rhi->resourceUpdate(this, resourceUpdates);
+}
+
+/*!
+ Records starting a new render pass targeting the render target \a rt.
+
+ \a resourceUpdates, when not null, specifies a resource update batch that
+ is to be committed and then released.
+
+ The color and depth/stencil buffers of the render target are normally
+ cleared. The clear values are specified in \a colorClearValue and \a
+ depthStencilClearValue. The exception is when the render target was created
+ with QRhiTextureRenderTarget::PreserveColorContents and/or
+ QRhiTextureRenderTarget::PreserveDepthStencilContents. The clear values are
+ ignored then.
+
+ \note Enabling preserved color or depth contents leads to decreased
+ performance depending on the underlying hardware. Mobile GPUs with tiled
+ architecture benefit from not having to reload the previous contents into
+ the tile buffer. Similarly, a QRhiTextureRenderTarget with a QRhiTexture as
+ the depth buffer is less efficient than a QRhiRenderBuffer since using a
+ depth texture triggers requiring writing the data out to it, while with
+ renderbuffers this is not needed (as the API does not allow sampling or
+ reading from a renderbuffer).
+
+ \note Do not assume that any state or resource bindings persist between
+ passes.
+
+ \note The QRhiCommandBuffer's \c set and \c draw functions can only be
+ called inside a pass. Also, with the exception of setGraphicsPipeline(),
+ they expect to have a pipeline set already on the command buffer.
+ Unspecified issues may arise otherwise, depending on the backend.
+ */
+void QRhiCommandBuffer::beginPass(QRhiRenderTarget *rt,
+ const QColor &colorClearValue,
+ const QRhiDepthStencilClearValue &depthStencilClearValue,
+ QRhiResourceUpdateBatch *resourceUpdates)
+{
+ m_rhi->beginPass(this, rt, colorClearValue, depthStencilClearValue, resourceUpdates);
+}
+
+/*!
+ Records ending the current render pass.
+
+ \a resourceUpdates, when not null, specifies a resource update batch that
+ is to be committed and then released.
+ */
+void QRhiCommandBuffer::endPass(QRhiResourceUpdateBatch *resourceUpdates)
+{
+ m_rhi->endPass(this, resourceUpdates);
+}
+
+/*!
+ Records setting a new graphics pipeline \a ps.
+
+ \note This function must be called before recording other \c set or \c draw
+ commands on the command buffer.
+
+ \note QRhi will optimize out unnecessary invocations within a pass, so
+ therefore overoptimizing to avoid calls to this function is not necessary
+ on the applications' side.
+
+ \note This function can only be called inside a pass, meaning between a
+ beginPass() end endPass() call.
+ */
+void QRhiCommandBuffer::setGraphicsPipeline(QRhiGraphicsPipeline *ps)
+{
+ m_rhi->setGraphicsPipeline(this, ps);
+}
+
+/*!
+ Records binding a set of shader resources, such as, uniform buffers or
+ textures, that are made visible to one or more shader stages.
+
+ \a srb can be null in which case the current graphics pipeline's associated
+ QRhiGraphicsPipeline::shaderResourceBindings() is used. When \a srb is
+ non-null, it must be
+ \l{QRhiShaderResourceBindings::isLayoutCompatible()}{layout-compatible},
+ meaning the layout (number of bindings, the type and binding number of each
+ binding) must fully match the QRhiShaderResourceBindings that was
+ associated with the pipeline at the time of calling
+ QRhiGraphicsPipeline::build().
+
+ There are cases when a seemingly unnecessary setShaderResources() call is
+ mandatory: when rebuilding a resource referenced from \a srb, for example
+ changing the size of a QRhiBuffer followed by a QRhiBuffer::build(), this
+ is the place where associated native objects (such as descriptor sets in
+ case of Vulkan) are updated to refer to the current native resources that
+ back the QRhiBuffer, QRhiTexture, QRhiSampler objects referenced from \a
+ srb. In this case setShaderResources() must be called even if \a srb is
+ the same as in the last call.
+
+ \a dynamicOffsets allows specifying buffer offsets for uniform buffers that
+ were associated with \a srb via
+ QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(). This is
+ different from providing the offset in the \a srb itself: dynamic offsets
+ do not require building a new QRhiShaderResourceBindings for every
+ different offset, can avoid writing the underlying descriptors (with
+ backends where applicable), and so they may be more efficient. Each element
+ of \a dynamicOffsets is a \c binding - \c offset pair.
+ \a dynamicOffsetCount specifies the number of elements in \a dynamicOffsets.
+
+ \note All offsets in \a dynamicOffsets must be byte aligned to the value
+ returned from QRhi::ubufAlignment().
+
+ \note QRhi will optimize out unnecessary invocations within a pass (taking
+ the conditions described above into account), so therefore overoptimizing
+ to avoid calls to this function is not necessary on the applications' side.
+
+ \note This function can only be called inside a pass, meaning between a
+ beginPass() end endPass() call.
+ */
+void QRhiCommandBuffer::setShaderResources(QRhiShaderResourceBindings *srb,
+ int dynamicOffsetCount,
+ const DynamicOffset *dynamicOffsets)
+{
+ m_rhi->setShaderResources(this, srb, dynamicOffsetCount, dynamicOffsets);
+}
+
+/*!
+ Records vertex input bindings.
+
+ The index buffer used by subsequent drawIndexed() commands is specified by
+ \a indexBuf, \a indexOffset, and \a indexFormat. \a indexBuf can be set to
+ null when indexed drawing is not needed.
+
+ Vertex buffer bindings are batched. \a startBinding specifies the first
+ binding number. The recorded command then binds each buffer from \a
+ bindings to the binding point \c{startBinding + i} where \c i is the index
+ in \a bindings. Each element in \a bindings specifies a QRhiBuffer and an
+ offset.
+
+ Superfluous vertex input and index changes in the same pass are ignored
+ automatically with most backends and therefore applications do not need to
+ overoptimize to avoid calls to this function.
+
+ \note This function can only be called inside a pass, meaning between a
+ beginPass() end endPass() call.
+
+ As a simple example, take a vertex shader with two inputs:
+
+ \badcode
+ layout(location = 0) in vec4 position;
+ layout(location = 1) in vec3 color;
+ \endcode
+
+ and assume we have the data available in interleaved format, using only 2
+ floats for position (so 5 floats per vertex: x, y, r, g, b). A QRhiGraphicsPipeline for
+ this shader can then be created using the input layout:
+
+ \badcode
+ QRhiVertexInputLayout inputLayout;
+ inputLayout.setBindings({
+ { 5 * sizeof(float) }
+ });
+ inputLayout.setAttributes({
+ { 0, 0, QRhiVertexInputAttribute::Float2, 0 },
+ { 0, 1, QRhiVertexInputAttribute::Float3, 2 * sizeof(float) }
+ });
+ \endcode
+
+ Here there is one buffer binding (binding number 0), with two inputs
+ referencing it. When recording the pass, once the pipeline is set, the
+ vertex bindings can be specified simply like the following (using C++11
+ initializer syntax), assuming vbuf is the QRhiBuffer with all the
+ interleaved position+color data:
+
+ \badcode
+ const QRhiCommandBuffer::VertexInput vbufBinding(vbuf, 0);
+ cb->setVertexInput(0, 1, &vbufBinding);
+ \endcode
+ */
+void QRhiCommandBuffer::setVertexInput(int startBinding, int bindingCount, const VertexInput *bindings,
+ QRhiBuffer *indexBuf, quint32 indexOffset,
+ IndexFormat indexFormat)
+{
+ m_rhi->setVertexInput(this, startBinding, bindingCount, bindings, indexBuf, indexOffset, indexFormat);
+}
+
+/*!
+ Records setting the active viewport rectangle specified in \a viewport.
+
+ With backends where the underlying graphics API has scissoring always
+ enabled, this function also sets the scissor to match the viewport whenever
+ the active QRhiGraphicsPipeline does not have
+ \l{QRhiGraphicsPipeline::UsesScissor}{UsesScissor} set.
+
+ \note QRhi assumes OpenGL-style viewport coordinates, meaning x and y are
+ bottom-left.
+
+ \note This function can only be called inside a pass, meaning between a
+ beginPass() end endPass() call.
+ */
+void QRhiCommandBuffer::setViewport(const QRhiViewport &viewport)
+{
+ m_rhi->setViewport(this, viewport);
+}
+
+/*!
+ Records setting the active scissor rectangle specified in \a scissor.
+
+ This can only be called when the bound pipeline has
+ \l{QRhiGraphicsPipeline::UsesScissor}{UsesScissor} set. When the flag is
+ set on the active pipeline, this function must be called because scissor
+ testing will get enabled and so a scissor rectangle must be provided.
+
+ \note QRhi assumes OpenGL-style viewport coordinates, meaning x and y are
+ bottom-left.
+
+ \note This function can only be called inside a pass, meaning between a
+ beginPass() end endPass() call.
+ */
+void QRhiCommandBuffer::setScissor(const QRhiScissor &scissor)
+{
+ m_rhi->setScissor(this, scissor);
+}
+
+/*!
+ Records setting the active blend constants to \a c.
+
+ This can only be called when the bound pipeline has
+ QRhiGraphicsPipeline::UsesBlendConstants set.
+
+ \note This function can only be called inside a pass, meaning between a
+ beginPass() end endPass() call.
+ */
+void QRhiCommandBuffer::setBlendConstants(const QColor &c)
+{
+ m_rhi->setBlendConstants(this, c);
+}
+
+/*!
+ Records setting the active stencil reference value to \a refValue.
+
+ This can only be called when the bound pipeline has
+ QRhiGraphicsPipeline::UsesStencilRef set.
+
+ \note This function can only be called inside a pass, meaning between a
+ beginPass() end endPass() call.
+ */
+void QRhiCommandBuffer::setStencilRef(quint32 refValue)
+{
+ m_rhi->setStencilRef(this, refValue);
+}
+
+/*!
+ Records a non-indexed draw.
+
+ The number of vertices is specified in \a vertexCount. For instanced
+ drawing set \a instanceCount to a value other than 1. \a firstVertex is
+ the index of the first vertex to draw. \a firstInstance is the instance ID
+ of the first instance to draw.
+
+ \note This function can only be called inside a pass, meaning between a
+ beginPass() end endPass() call.
+ */
+void QRhiCommandBuffer::draw(quint32 vertexCount,
+ quint32 instanceCount, quint32 firstVertex, quint32 firstInstance)
+{
+ m_rhi->draw(this, vertexCount, instanceCount, firstVertex, firstInstance);
+}
+
+/*!
+ Records an indexed draw.
+
+ The number of vertices is specified in \a indexCount. \a firstIndex is the
+ base index. The effective offset in the index buffer is given by
+ \c{indexOffset + firstIndex * n} where \c n is 2 or 4 depending on the
+ index element type. \c indexOffset is specified in setVertexInput().
+
+ \note The effective offset in the index buffer must be 4 byte aligned with
+ some backends (for example, Metal). With these backends the
+ \l{QRhi::NonFourAlignedEffectiveIndexBufferOffset}{NonFourAlignedEffectiveIndexBufferOffset}
+ feature will be reported as not-supported.
+
+ For instanced drawing set \a instanceCount to a value other than 1. \a
+ firstInstance is the instance ID of the first instance to draw.
+
+ \a vertexOffset is added to the vertex index.
+
+ \note This function can only be called inside a pass, meaning between a
+ beginPass() end endPass() call.
+ */
+void QRhiCommandBuffer::drawIndexed(quint32 indexCount,
+ quint32 instanceCount, quint32 firstIndex,
+ qint32 vertexOffset, quint32 firstInstance)
+{
+ m_rhi->drawIndexed(this, indexCount, instanceCount, firstIndex, vertexOffset, firstInstance);
+}
+
+/*!
+ Records a named debug group on the command buffer. This is shown in
+ graphics debugging tools such as \l{https://renderdoc.org/}{RenderDoc} and
+ \l{https://developer.apple.com/xcode/}{XCode}. The end of the grouping is
+ indicated by debugMarkEnd().
+
+ \note Ignored when QRhi::DebugMarkers are not supported or
+ QRhi::EnableDebugMarkers is not set.
+
+ \note Can be called anywhere within the frame, both inside and outside of passes.
+ */
+void QRhiCommandBuffer::debugMarkBegin(const QByteArray &name)
+{
+ m_rhi->debugMarkBegin(this, name);
+}
+
+/*!
+ Records the end of a debug group.
+
+ \note Ignored when QRhi::DebugMarkers are not supported or
+ QRhi::EnableDebugMarkers is not set.
+
+ \note Can be called anywhere within the frame, both inside and outside of passes.
+ */
+void QRhiCommandBuffer::debugMarkEnd()
+{
+ m_rhi->debugMarkEnd(this);
+}
+
+/*!
+ Inserts a debug message \a msg into the command stream.
+
+ \note Ignored when QRhi::DebugMarkers are not supported or
+ QRhi::EnableDebugMarkers is not set.
+
+ \note With some backends debugMarkMsg() is only supported inside a pass and
+ is ignored when called outside a pass. With others it is recorded anywhere
+ within the frame.
+ */
+void QRhiCommandBuffer::debugMarkMsg(const QByteArray &msg)
+{
+ m_rhi->debugMarkMsg(this, msg);
+}
+
+/*!
+ \return a pointer to a backend-specific QRhiNativeHandles subclass, such as
+ QRhiVulkanCommandBufferNativeHandles. The returned value is null when
+ exposing the underlying native resources is not supported by, or not
+ applicable to, the backend.
+
+ \sa QRhiVulkanCommandBufferNativeHandles,
+ QRhiMetalCommandBufferNativeHandles, beginExternal(), endExternal()
+ */
+const QRhiNativeHandles *QRhiCommandBuffer::nativeHandles()
+{
+ return m_rhi->nativeHandles(this);
+}
+
+/*!
+ To be called when the application before the application is about to
+ enqueue commands to the current pass' command buffer by calling graphics
+ API functions directly.
+
+ With Vulkan or Metal one can query the native command buffer or encoder
+ objects via nativeHandles() and enqueue commands to them. With OpenGL or
+ Direct3D 11 the (device) context can be retrieved from
+ QRhi::nativeHandles(). However, this must never be done without ensuring
+ the QRhiCommandBuffer's state stays up-to-date. Hence the requirement for
+ wrapping any externally added command recording between beginExternal() and
+ endExternal(). Conceptually this is the same as QPainter's
+ \l{QPainter::beginNativePainting()}{beginNativePainting()} and
+ \l{QPainter::endNativePainting()}{endNativePainting()} functions.
+
+ For OpenGL in particular, this function has an additional task: it makes
+ sure the context is made current on the current thread.
+
+ \note Once beginExternal() is called, no other render pass specific
+ functions (\c set* or \c draw*) must be called on the
+ QRhiCommandBuffer until endExternal().
+
+ \sa endExternal(), nativeHandles()
+ */
+void QRhiCommandBuffer::beginExternal()
+{
+ m_rhi->beginExternal(this);
+}
+
+/*!
+ To be called once the externally added commands are recorded to the command
+ buffer or context.
+
+ \note All QRhiCommandBuffer state must be assumed as invalid after calling
+ this function. Pipelines, vertex and index buffers, and other state must be
+ set again if more draw calls are recorded after the external commands.
+
+ \sa beginExternal(), nativeHandles()
+ */
+void QRhiCommandBuffer::endExternal()
+{
+ m_rhi->endExternal(this);
+}
+
+/*!
+ \return the value (typically an offset) \a v aligned to the uniform buffer
+ alignment given by by ubufAlignment().
+ */
+int QRhi::ubufAligned(int v) const
+{
+ const int byteAlign = ubufAlignment();
+ return (v + byteAlign - 1) & ~(byteAlign - 1);
+}
+
+/*!
+ \return the number of mip levels for a given \a size.
+ */
+int QRhi::mipLevelsForSize(const QSize &size) const
+{
+ return qFloor(std::log2(qMax(size.width(), size.height()))) + 1;
+}
+
+/*!
+ \return the texture image size for a given \a mipLevel, calculated based on
+ the level 0 size given in \a baseLevelSize.
+ */
+QSize QRhi::sizeForMipLevel(int mipLevel, const QSize &baseLevelSize) const
+{
+ const int w = qMax(1, baseLevelSize.width() >> mipLevel);
+ const int h = qMax(1, baseLevelSize.height() >> mipLevel);
+ return QSize(w, h);
+}
+
+/*!
+ \return \c true if the underlying graphics API has the Y axis pointing up
+ in framebuffers and images.
+
+ In practice this is \c true for OpenGL only.
+ */
+bool QRhi::isYUpInFramebuffer() const
+{
+ return d->isYUpInFramebuffer();
+}
+
+/*!
+ \return \c true if the underlying graphics API has the Y axis pointing up
+ in its normalized device coordinate system.
+
+ In practice this is \c false for Vulkan only.
+
+ \note clipSpaceCorrMatrix() includes the corresponding adjustment (to make
+ Y point up) in its returned matrix.
+ */
+bool QRhi::isYUpInNDC() const
+{
+ return d->isYUpInNDC();
+}
+
+/*!
+ \return \c true if the underlying graphics API uses depth 0 - 1 in clip
+ space.
+
+ In practice this is \c false for OpenGL only.
+
+ \note clipSpaceCorrMatrix() includes the corresponding adjustment in its
+ returned matrix.
+ */
+bool QRhi::isClipDepthZeroToOne() const
+{
+ return d->isClipDepthZeroToOne();
+}
+
+/*!
+ \return a matrix that can be used to allow applications keep using
+ OpenGL-targeted vertex data and perspective projection matrices (such as,
+ the ones generated by QMatrix4x4::perspective()), regardless of the
+ backend. Once \c{this_matrix * mvp} is used instead of just \c mvp, vertex
+ data with Y up and viewports with depth range 0 - 1 can be used without
+ considering what backend and so graphics API is going to be used at run
+ time.
+
+ See
+ \l{https://matthewwellings.com/blog/the-new-vulkan-coordinate-system/}{this
+ page} for a discussion of the topic from Vulkan perspective.
+ */
+QMatrix4x4 QRhi::clipSpaceCorrMatrix() const
+{
+ return d->clipSpaceCorrMatrix();
+}
+
+/*!
+ \return \c true if the specified texture \a format modified by \a flags is
+ supported.
+
+ The query is supported both for uncompressed and compressed formats.
+ */
+bool QRhi::isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const
+{
+ return d->isTextureFormatSupported(format, flags);
+}
+
+/*!
+ \return \c true if the specified \a feature is supported
+ */
+bool QRhi::isFeatureSupported(QRhi::Feature feature) const
+{
+ return d->isFeatureSupported(feature);
+}
+
+/*!
+ \return the value for the specified resource \a limit.
+
+ The values are expected to be queried by the backends upon initialization,
+ meaning calling this function is a light operation.
+ */
+int QRhi::resourceLimit(ResourceLimit limit) const
+{
+ return d->resourceLimit(limit);
+}
+
+/*!
+ \return a pointer to the backend-specific collection of native objects
+ for the device, context, and similar concepts used by the backend.
+
+ Cast to QRhiVulkanNativeHandles, QRhiD3D11NativeHandles,
+ QRhiGles2NativeHandles, QRhiMetalNativeHandles as appropriate.
+
+ \note No ownership is transferred, neither for the returned pointer nor for
+ any native objects.
+ */
+const QRhiNativeHandles *QRhi::nativeHandles()
+{
+ return d->nativeHandles();
+}
+
+/*!
+ \return the associated QRhiProfiler instance.
+
+ An instance is always available for each QRhi, but it is not very useful
+ without EnableProfiling because no data is collected without setting the
+ flag upon creation.
+ */
+QRhiProfiler *QRhi::profiler()
+{
+ return &d->profiler;
+}
+
+/*!
+ \return a new graphics pipeline resource.
+
+ \sa QRhiResource::release()
+ */
+QRhiGraphicsPipeline *QRhi::newGraphicsPipeline()
+{
+ return d->createGraphicsPipeline();
+}
+
+/*!
+ \return a new shader resource binding collection resource.
+
+ \sa QRhiResource::release()
+ */
+QRhiShaderResourceBindings *QRhi::newShaderResourceBindings()
+{
+ return d->createShaderResourceBindings();
+}
+
+/*!
+ \return a new buffer with the specified \a type, \a usage, and \a size.
+
+ \note Some \a usage and \a type combinations may not be supported by all
+ backends. See \l{QRhi::NonDynamicUniformBuffers}{the feature flags}.
+
+ \sa QRhiResource::release()
+ */
+QRhiBuffer *QRhi::newBuffer(QRhiBuffer::Type type,
+ QRhiBuffer::UsageFlags usage,
+ int size)
+{
+ return d->createBuffer(type, usage, size);
+}
+
+/*!
+ \return a new renderbuffer with the specified \a type, \a pixelSize, \a
+ sampleCount, and \a flags.
+
+ \sa QRhiResource::release()
+ */
+QRhiRenderBuffer *QRhi::newRenderBuffer(QRhiRenderBuffer::Type type,
+ const QSize &pixelSize,
+ int sampleCount,
+ QRhiRenderBuffer::Flags flags)
+{
+ return d->createRenderBuffer(type, pixelSize, sampleCount, flags);
+}
+
+/*!
+ \return a new texture with the specified \a format, \a pixelSize, \a
+ sampleCount, and \a flags.
+
+ \note \a format specifies the requested internal and external format,
+ meaning the data to be uploaded to the texture will need to be in a
+ compatible format, while the native texture may (but is not guaranteed to,
+ in case of OpenGL at least) use this format internally.
+
+ \sa QRhiResource::release()
+ */
+QRhiTexture *QRhi::newTexture(QRhiTexture::Format format,
+ const QSize &pixelSize,
+ int sampleCount,
+ QRhiTexture::Flags flags)
+{
+ return d->createTexture(format, pixelSize, sampleCount, flags);
+}
+
+/*!
+ \return a new sampler with the specified magnification filter \a magFilter,
+ minification filter \a minFilter, mipmapping mode \a mipmapMpde, and S/T
+ addressing modes \a u and \a v.
+
+ \sa QRhiResource::release()
+ */
+QRhiSampler *QRhi::newSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
+ QRhiSampler::Filter mipmapMode,
+ QRhiSampler:: AddressMode u, QRhiSampler::AddressMode v)
+{
+ return d->createSampler(magFilter, minFilter, mipmapMode, u, v);
+}
+
+/*!
+ \return a new texture render target with color and depth/stencil
+ attachments given in \a desc, and with the specified \a flags.
+
+ \sa QRhiResource::release()
+ */
+
+QRhiTextureRenderTarget *QRhi::newTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
+ QRhiTextureRenderTarget::Flags flags)
+{
+ return d->createTextureRenderTarget(desc, flags);
+}
+
+/*!
+ \return a new swapchain.
+
+ \sa QRhiResource::release(), QRhiSwapChain::buildOrResize()
+ */
+QRhiSwapChain *QRhi::newSwapChain()
+{
+ return d->createSwapChain();
+}
+
+/*!
+ Starts a new frame targeting the next available buffer of \a swapChain.
+
+ The high level pattern of rendering into a QWindow using a swapchain:
+
+ \list
+
+ \li Create a swapchain.
+
+ \li Call QRhiSwapChain::buildOrResize() whenever the surface size is
+ different than before.
+
+ \li Call QRhiSwapChain::release() on
+ QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed.
+
+ \li Then on every frame:
+ \badcode
+ beginFrame(sc);
+ updates = nextResourceUpdateBatch();
+ updates->...
+ QRhiCommandBuffer *cb = sc->currentFrameCommandBuffer();
+ cb->beginPass(sc->currentFrameRenderTarget(), colorClear, dsClear, updates);
+ ...
+ cb->endPass();
+ ... // more passes as necessary
+ endFrame(sc);
+ \endcode
+
+ \endlist
+
+ \a flags is currently unused.
+
+ \sa endFrame()
+ */
+QRhi::FrameOpResult QRhi::beginFrame(QRhiSwapChain *swapChain, BeginFrameFlags flags)
+{
+ if (d->inFrame)
+ qWarning("Attempted to call beginFrame() within a still active frame; ignored");
+
+ QRhi::FrameOpResult r = !d->inFrame ? d->beginFrame(swapChain, flags) : FrameOpSuccess;
+ if (r == FrameOpSuccess)
+ d->inFrame = true;
+
+ return r;
+}
+
+/*!
+ Ends, commits, and presents a frame that was started in the last
+ beginFrame() on \a swapChain.
+
+ Double (or triple) buffering is managed internally by the QRhiSwapChain and
+ QRhi.
+
+ \a flags can optionally be used to change the behavior in certain ways.
+ Passing QRhi::SkipPresent skips queuing the Present command or calling
+ swapBuffers.
+
+ \sa beginFrame()
+ */
+QRhi::FrameOpResult QRhi::endFrame(QRhiSwapChain *swapChain, EndFrameFlags flags)
+{
+ if (!d->inFrame)
+ qWarning("Attempted to call endFrame() without an active frame; ignored");
+
+ QRhi::FrameOpResult r = d->inFrame ? d->endFrame(swapChain, flags) : FrameOpSuccess;
+ d->inFrame = false;
+ // releaseAndDestroyLater is a high level QRhi concept the backends know
+ // nothing about - handle it here.
+ qDeleteAll(d->pendingReleaseAndDestroyResources);
+ d->pendingReleaseAndDestroyResources.clear();
+
+ return r;
+}
+
+/*!
+ \return true when there is an active frame, meaning there was a
+ beginFrame() (or beginOffscreenFrame()) with no corresponding endFrame()
+ (or endOffscreenFrame()) yet.
+
+ \sa currentFrameSlot(), beginFrame(), endFrame()
+ */
+bool QRhi::isRecordingFrame() const
+{
+ return d->inFrame;
+}
+
+/*!
+ \return the current frame slot index while recording a frame. Unspecified
+ when called outside an active frame (that is, when isRecordingFrame() is \c
+ false).
+
+ With backends like Vulkan or Metal, it is the responsibility of the QRhi
+ backend to block whenever starting a new frame and finding the CPU is
+ already \c{FramesInFlight - 1} frames ahead of the GPU (because the command
+ buffer submitted in frame no. \c{current} - \c{FramesInFlight} has not yet
+ completed).
+
+ Resources that tend to change between frames (such as, the native buffer
+ object backing a QRhiBuffer with type QRhiBuffer::Dynamic) exist in
+ multiple versions, so that each frame, that can be submitted while a
+ previous one is still being processed, works with its own copy, thus
+ avoiding the need to stall the pipeline when preparing the frame. (The
+ contents of a resource that may still be in use in the GPU should not be
+ touched, but simply always waiting for the previous frame to finish would
+ reduce GPU utilization and ultimately, performance and efficiency.)
+
+ Conceptually this is somewhat similar to copy-on-write schemes used by some
+ C++ containers and other types. It may also be similar to what an OpenGL or
+ Direct 3D 11 implementation performs internally for certain type of objects.
+
+ In practice, such double (or tripple) buffering resources is realized in
+ the Vulkan, Metal, and similar QRhi backends by having a fixed number of
+ native resource (such as, VkBuffer) \c slots behind a QRhiResource. That
+ can then be indexed by a frame slot index running 0, 1, ..,
+ FramesInFlight-1, and then wrapping around.
+
+ All this is managed transparently to the users of QRhi. However,
+ applications that integrate rendering done directly with the graphics API
+ may want to perform a similar double or tripple buffering of their own
+ graphics resources. That is then most easily achieved by knowing the values
+ of the maximum number of in-flight frames (retrievable via resourceLimit())
+ and the current frame (slot) index (returned by this function).
+
+ \sa isRecordingFrame(), beginFrame(), endFrame()
+ */
+int QRhi::currentFrameSlot() const
+{
+ return d->currentFrameSlot;
+}
+
+/*!
+ Starts a new offscreen frame. Provides a command buffer suitable for
+ recording rendering commands in \a cb.
+
+ \note The QRhiCommandBuffer stored to *cb is not owned by the caller.
+
+ Rendering without a swapchain is possible as well. The typical use case is
+ to use it in completely offscreen applications, e.g. to generate image
+ sequences by rendering and reading back without ever showing a window.
+
+ Usage in on-screen applications (so beginFrame, endFrame,
+ beginOffscreenFrame, endOffscreenFrame, beginFrame, ...) is possible too
+ but it does reduce parallelism so it should be done only infrequently.
+
+ Offscreen frames do not let the CPU - potentially - generate another frame
+ while the GPU is still processing the previous one. This has the side
+ effect that if readbacks are scheduled, the results are guaranteed to be
+ available once endOffscreenFrame() returns. That is not the case with
+ frames targeting a swapchain.
+
+ The skeleton of rendering a frame without a swapchain and then reading the
+ frame contents back could look like the following:
+
+ \badcode
+ QRhiReadbackResult rbResult;
+ QRhiCommandBuffer *cb;
+ beginOffscreenFrame(&cb);
+ beginPass
+ ...
+ u = nextResourceUpdateBatch();
+ u->readBackTexture(rb, &rbResult);
+ endPass(u);
+ endOffscreenFrame();
+ // image data available in rbResult
+ \endcode
+
+ \sa endOffscreenFrame()
+ */
+QRhi::FrameOpResult QRhi::beginOffscreenFrame(QRhiCommandBuffer **cb)
+{
+ if (d->inFrame)
+ qWarning("Attempted to call beginOffscreenFrame() within a still active frame; ignored");
+
+ QRhi::FrameOpResult r = !d->inFrame ? d->beginOffscreenFrame(cb) : FrameOpSuccess;
+ if (r == FrameOpSuccess)
+ d->inFrame = true;
+
+ return r;
+}
+
+/*!
+ Ends and waits for the offscreen frame.
+
+ \sa beginOffscreenFrame()
+ */
+QRhi::FrameOpResult QRhi::endOffscreenFrame()
+{
+ if (!d->inFrame)
+ qWarning("Attempted to call endOffscreenFrame() without an active frame; ignored");
+
+ QRhi::FrameOpResult r = d->inFrame ? d->endOffscreenFrame() : FrameOpSuccess;
+ d->inFrame = false;
+ qDeleteAll(d->pendingReleaseAndDestroyResources);
+ d->pendingReleaseAndDestroyResources.clear();
+
+ return r;
+}
+
+/*!
+ Waits for any work on the graphics queue (where applicable) to complete,
+ then executes all deferred operations, like completing readbacks and
+ resource releases. Can be called inside and outside of a frame, but not
+ inside a pass. Inside a frame it implies submitting any work on the
+ command buffer.
+
+ \note Avoid this function. One case where it may be needed is when the
+ results of an enqueued readback in a swapchain-based frame are needed at a
+ fixed given point and so waiting for the results is desired.
+ */
+QRhi::FrameOpResult QRhi::finish()
+{
+ return d->finish();
+}
+
+/*!
+ \return the list of supported sample counts.
+
+ A typical example would be (1, 2, 4, 8).
+
+ With some backend this list of supported values is fixed in advance, while
+ with some others the (physical) device properties indicate what is
+ supported at run time.
+ */
+QVector<int> QRhi::supportedSampleCounts() const
+{
+ return d->supportedSampleCounts();
+}
+
+/*!
+ \return the minimum uniform buffer offset alignment in bytes. This is
+ typically 256.
+
+ Attempting to bind a uniform buffer region with an offset not aligned to
+ this value will lead to failures depending on the backend and the
+ underlying graphics API.
+
+ \sa ubufAligned()
+ */
+int QRhi::ubufAlignment() const
+{
+ return d->ubufAlignment();
+}
+
+QRhiGlobalObjectIdGenerator::Type QRhiGlobalObjectIdGenerator::newId()
+{
+ static QRhiGlobalObjectIdGenerator inst;
+ return ++inst.counter;
+}
+
+bool QRhiPassResourceTracker::isEmpty() const
+{
+ return m_buffers.isEmpty() && m_textures.isEmpty();
+}
+
+void QRhiPassResourceTracker::reset()
+{
+ m_buffers.clear();
+ m_textures.clear();
+}
+
+static inline QRhiPassResourceTracker::BufferStage earlierStage(QRhiPassResourceTracker::BufferStage a,
+ QRhiPassResourceTracker::BufferStage b)
+{
+ return QRhiPassResourceTracker::BufferStage(qMin(int(a), int(b)));
+}
+
+void QRhiPassResourceTracker::registerBufferOnce(QRhiBuffer *buf, int slot, BufferAccess access, BufferStage stage,
+ const UsageState &stateAtPassBegin)
+{
+ auto it = std::find_if(m_buffers.begin(), m_buffers.end(), [buf](const Buffer &b) { return b.buf == buf; });
+ if (it != m_buffers.end()) {
+ if (it->access != access) {
+ const QByteArray name = buf->name();
+ qWarning("Buffer %p (%s) used with different accesses within the same pass, this is not allowed.",
+ buf, name.constData());
+ return;
+ }
+ if (it->stage != stage)
+ it->stage = earlierStage(it->stage, stage);
+ // Multiple registrations of the same buffer is fine as long is it is
+ // a compatible usage. stateAtPassBegin is not actually the state at
+ // pass begin in the second, third, etc. invocation but that's fine
+ // since we'll just return here.
+ return;
+ }
+
+ Buffer b;
+ b.buf = buf;
+ b.slot = slot;
+ b.access = access;
+ b.stage = stage;
+ b.stateAtPassBegin = stateAtPassBegin;
+ m_buffers.append(b);
+}
+
+static inline QRhiPassResourceTracker::TextureStage earlierStage(QRhiPassResourceTracker::TextureStage a,
+ QRhiPassResourceTracker::TextureStage b)
+{
+ return QRhiPassResourceTracker::TextureStage(qMin(int(a), int(b)));
+}
+
+void QRhiPassResourceTracker::registerTextureOnce(QRhiTexture *tex, TextureAccess access, TextureStage stage,
+ const UsageState &stateAtPassBegin)
+{
+ auto it = std::find_if(m_textures.begin(), m_textures.end(), [tex](const Texture &t) { return t.tex == tex; });
+ if (it != m_textures.end()) {
+ if (it->access != access) {
+ const QByteArray name = tex->name();
+ qWarning("Texture %p (%s) used with different accesses within the same pass, this is not allowed.",
+ tex, name.constData());
+ }
+ if (it->stage != stage)
+ it->stage = earlierStage(it->stage, stage);
+ // Multiple registrations of the same texture is fine as long is it is
+ // a compatible usage. stateAtPassBegin is not actually the state at
+ // pass begin in the second, third, etc. invocation but that's fine
+ // since we'll just return here.
+ return;
+ }
+
+ Texture t;
+ t.tex = tex;
+ t.access = access;
+ t.stage = stage;
+ t.stateAtPassBegin = stateAtPassBegin;
+ m_textures.append(t);
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/rhi/qrhi_p.h b/src/gui/rhi/qrhi_p.h
new file mode 100644
index 0000000000..a0f57819e4
--- /dev/null
+++ b/src/gui/rhi/qrhi_p.h
@@ -0,0 +1,1375 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gui module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QRHI_H
+#define QRHI_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtGui/qtguiglobal.h>
+#include <QSize>
+#include <QMatrix4x4>
+#include <QVector>
+#include <QThread>
+#include <QColor>
+#include <QImage>
+#include <functional>
+#include <array>
+#include <private/qshader_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QWindow;
+class QRhiImplementation;
+class QRhiBuffer;
+class QRhiRenderBuffer;
+class QRhiTexture;
+class QRhiSampler;
+class QRhiCommandBuffer;
+class QRhiResourceUpdateBatch;
+class QRhiResourceUpdateBatchPrivate;
+class QRhiProfiler;
+class QRhiShaderResourceBindingPrivate;
+
+class Q_GUI_EXPORT QRhiDepthStencilClearValue
+{
+public:
+ QRhiDepthStencilClearValue() = default;
+ QRhiDepthStencilClearValue(float d, quint32 s);
+
+ float depthClearValue() const { return m_d; }
+ void setDepthClearValue(float d) { m_d = d; }
+
+ quint32 stencilClearValue() const { return m_s; }
+ void setStencilClearValue(quint32 s) { m_s = s; }
+
+private:
+ float m_d = 1.0f;
+ quint32 m_s = 0;
+};
+
+Q_DECLARE_TYPEINFO(QRhiDepthStencilClearValue, Q_MOVABLE_TYPE);
+
+Q_GUI_EXPORT bool operator==(const QRhiDepthStencilClearValue &a, const QRhiDepthStencilClearValue &b) Q_DECL_NOTHROW;
+Q_GUI_EXPORT bool operator!=(const QRhiDepthStencilClearValue &a, const QRhiDepthStencilClearValue &b) Q_DECL_NOTHROW;
+Q_GUI_EXPORT uint qHash(const QRhiDepthStencilClearValue &v, uint seed = 0) Q_DECL_NOTHROW;
+#ifndef QT_NO_DEBUG_STREAM
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiDepthStencilClearValue &);
+#endif
+
+class Q_GUI_EXPORT QRhiViewport
+{
+public:
+ QRhiViewport() = default;
+ QRhiViewport(float x, float y, float w, float h, float minDepth = 0.0f, float maxDepth = 1.0f);
+
+ std::array<float, 4> viewport() const { return m_rect; }
+ void setViewport(float x, float y, float w, float h) {
+ m_rect[0] = x; m_rect[1] = y; m_rect[2] = w; m_rect[3] = h;
+ }
+
+ float minDepth() const { return m_minDepth; }
+ void setMinDepth(float minDepth) { m_minDepth = minDepth; }
+
+ float maxDepth() const { return m_maxDepth; }
+ void setMaxDepth(float maxDepth) { m_maxDepth = maxDepth; }
+
+private:
+ std::array<float, 4> m_rect { { 0.0f, 0.0f, 0.0f, 0.0f } };
+ float m_minDepth = 0.0f;
+ float m_maxDepth = 1.0f;
+};
+
+Q_DECLARE_TYPEINFO(QRhiViewport, Q_MOVABLE_TYPE);
+
+Q_GUI_EXPORT bool operator==(const QRhiViewport &a, const QRhiViewport &b) Q_DECL_NOTHROW;
+Q_GUI_EXPORT bool operator!=(const QRhiViewport &a, const QRhiViewport &b) Q_DECL_NOTHROW;
+Q_GUI_EXPORT uint qHash(const QRhiViewport &v, uint seed = 0) Q_DECL_NOTHROW;
+#ifndef QT_NO_DEBUG_STREAM
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiViewport &);
+#endif
+
+class Q_GUI_EXPORT QRhiScissor
+{
+public:
+ QRhiScissor() = default;
+ QRhiScissor(int x, int y, int w, int h);
+
+ std::array<int, 4> scissor() const { return m_rect; }
+ void setScissor(int x, int y, int w, int h) {
+ m_rect[0] = x; m_rect[1] = y; m_rect[2] = w; m_rect[3] = h;
+ }
+
+private:
+ std::array<int, 4> m_rect { { 0, 0, 0, 0 } };
+};
+
+Q_DECLARE_TYPEINFO(QRhiScissor, Q_MOVABLE_TYPE);
+
+Q_GUI_EXPORT bool operator==(const QRhiScissor &a, const QRhiScissor &b) Q_DECL_NOTHROW;
+Q_GUI_EXPORT bool operator!=(const QRhiScissor &a, const QRhiScissor &b) Q_DECL_NOTHROW;
+Q_GUI_EXPORT uint qHash(const QRhiScissor &v, uint seed = 0) Q_DECL_NOTHROW;
+#ifndef QT_NO_DEBUG_STREAM
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiScissor &);
+#endif
+
+class Q_GUI_EXPORT QRhiVertexInputBinding
+{
+public:
+ enum Classification {
+ PerVertex,
+ PerInstance
+ };
+
+ QRhiVertexInputBinding() = default;
+ QRhiVertexInputBinding(quint32 stride, Classification cls = PerVertex, int stepRate = 1);
+
+ quint32 stride() const { return m_stride; }
+ void setStride(quint32 s) { m_stride = s; }
+
+ Classification classification() const { return m_classification; }
+ void setClassification(Classification c) { m_classification = c; }
+
+ int instanceStepRate() const { return m_instanceStepRate; }
+ void setInstanceStepRate(int rate) { m_instanceStepRate = rate; }
+
+private:
+ quint32 m_stride = 0;
+ Classification m_classification = PerVertex;
+ int m_instanceStepRate = 1;
+};
+
+Q_DECLARE_TYPEINFO(QRhiVertexInputBinding, Q_MOVABLE_TYPE);
+
+Q_GUI_EXPORT bool operator==(const QRhiVertexInputBinding &a, const QRhiVertexInputBinding &b) Q_DECL_NOTHROW;
+Q_GUI_EXPORT bool operator!=(const QRhiVertexInputBinding &a, const QRhiVertexInputBinding &b) Q_DECL_NOTHROW;
+Q_GUI_EXPORT uint qHash(const QRhiVertexInputBinding &v, uint seed = 0) Q_DECL_NOTHROW;
+#ifndef QT_NO_DEBUG_STREAM
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiVertexInputBinding &);
+#endif
+
+class Q_GUI_EXPORT QRhiVertexInputAttribute
+{
+public:
+ enum Format {
+ Float4,
+ Float3,
+ Float2,
+ Float,
+ UNormByte4,
+ UNormByte2,
+ UNormByte
+ };
+
+ QRhiVertexInputAttribute() = default;
+ QRhiVertexInputAttribute(int binding, int location, Format format, quint32 offset);
+
+ int binding() const { return m_binding; }
+ void setBinding(int b) { m_binding = b; }
+
+ int location() const { return m_location; }
+ void setLocation(int loc) { m_location = loc; }
+
+ Format format() const { return m_format; }
+ void setFormt(Format f) { m_format = f; }
+
+ quint32 offset() const { return m_offset; }
+ void setOffset(quint32 ofs) { m_offset = ofs; }
+
+private:
+ int m_binding = 0;
+ int m_location = 0;
+ Format m_format = Float4;
+ quint32 m_offset = 0;
+};
+
+Q_DECLARE_TYPEINFO(QRhiVertexInputAttribute, Q_MOVABLE_TYPE);
+
+Q_GUI_EXPORT bool operator==(const QRhiVertexInputAttribute &a, const QRhiVertexInputAttribute &b) Q_DECL_NOTHROW;
+Q_GUI_EXPORT bool operator!=(const QRhiVertexInputAttribute &a, const QRhiVertexInputAttribute &b) Q_DECL_NOTHROW;
+Q_GUI_EXPORT uint qHash(const QRhiVertexInputAttribute &v, uint seed = 0) Q_DECL_NOTHROW;
+#ifndef QT_NO_DEBUG_STREAM
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiVertexInputAttribute &);
+#endif
+
+class Q_GUI_EXPORT QRhiVertexInputLayout
+{
+public:
+ QRhiVertexInputLayout() = default;
+
+ QVector<QRhiVertexInputBinding> bindings() const { return m_bindings; }
+ void setBindings(const QVector<QRhiVertexInputBinding> &v) { m_bindings = v; }
+
+ QVector<QRhiVertexInputAttribute> attributes() const { return m_attributes; }
+ void setAttributes(const QVector<QRhiVertexInputAttribute> &v) { m_attributes = v; }
+
+private:
+ QVector<QRhiVertexInputBinding> m_bindings;
+ QVector<QRhiVertexInputAttribute> m_attributes;
+};
+
+Q_DECLARE_TYPEINFO(QRhiVertexInputLayout, Q_MOVABLE_TYPE);
+
+Q_GUI_EXPORT bool operator==(const QRhiVertexInputLayout &a, const QRhiVertexInputLayout &b) Q_DECL_NOTHROW;
+Q_GUI_EXPORT bool operator!=(const QRhiVertexInputLayout &a, const QRhiVertexInputLayout &b) Q_DECL_NOTHROW;
+Q_GUI_EXPORT uint qHash(const QRhiVertexInputLayout &v, uint seed = 0) Q_DECL_NOTHROW;
+#ifndef QT_NO_DEBUG_STREAM
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiVertexInputLayout &);
+#endif
+
+class Q_GUI_EXPORT QRhiGraphicsShaderStage
+{
+public:
+ enum Type {
+ Vertex,
+ Fragment
+ };
+
+ QRhiGraphicsShaderStage() = default;
+ QRhiGraphicsShaderStage(Type type, const QShader &shader,
+ QShader::Variant v = QShader::StandardShader);
+
+ Type type() const { return m_type; }
+ void setType(Type t) { m_type = t; }
+
+ QShader shader() const { return m_shader; }
+ void setShader(const QShader &s) { m_shader = s; }
+
+ QShader::Variant shaderVariant() const { return m_shaderVariant; }
+ void setShaderVariant(QShader::Variant v) { m_shaderVariant = v; }
+
+private:
+ Type m_type = Vertex;
+ QShader m_shader;
+ QShader::Variant m_shaderVariant = QShader::StandardShader;
+};
+
+Q_DECLARE_TYPEINFO(QRhiGraphicsShaderStage, Q_MOVABLE_TYPE);
+
+Q_GUI_EXPORT bool operator==(const QRhiGraphicsShaderStage &a, const QRhiGraphicsShaderStage &b) Q_DECL_NOTHROW;
+Q_GUI_EXPORT bool operator!=(const QRhiGraphicsShaderStage &a, const QRhiGraphicsShaderStage &b) Q_DECL_NOTHROW;
+Q_GUI_EXPORT uint qHash(const QRhiGraphicsShaderStage &s, uint seed = 0) Q_DECL_NOTHROW;
+#ifndef QT_NO_DEBUG_STREAM
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiGraphicsShaderStage &);
+#endif
+
+class Q_GUI_EXPORT QRhiShaderResourceBinding
+{
+public:
+ enum Type {
+ UniformBuffer,
+ SampledTexture
+ };
+
+ enum StageFlag {
+ VertexStage = 1 << 0,
+ FragmentStage = 1 << 1
+ };
+ Q_DECLARE_FLAGS(StageFlags, StageFlag)
+
+ QRhiShaderResourceBinding();
+ QRhiShaderResourceBinding(const QRhiShaderResourceBinding &other);
+ QRhiShaderResourceBinding &operator=(const QRhiShaderResourceBinding &other);
+ ~QRhiShaderResourceBinding();
+ void detach();
+
+ bool isLayoutCompatible(const QRhiShaderResourceBinding &other) const;
+
+ static QRhiShaderResourceBinding uniformBuffer(int binding, StageFlags stage, QRhiBuffer *buf);
+ static QRhiShaderResourceBinding uniformBuffer(int binding, StageFlags stage, QRhiBuffer *buf, int offset, int size);
+ static QRhiShaderResourceBinding uniformBufferWithDynamicOffset(int binding, StageFlags stage, QRhiBuffer *buf, int size);
+ static QRhiShaderResourceBinding sampledTexture(int binding, StageFlags stage, QRhiTexture *tex, QRhiSampler *sampler);
+
+private:
+ QRhiShaderResourceBindingPrivate *d;
+ friend class QRhiShaderResourceBindingPrivate;
+ friend Q_GUI_EXPORT bool operator==(const QRhiShaderResourceBinding &, const QRhiShaderResourceBinding &) Q_DECL_NOTHROW;
+ friend Q_GUI_EXPORT bool operator!=(const QRhiShaderResourceBinding &, const QRhiShaderResourceBinding &) Q_DECL_NOTHROW;
+ friend Q_GUI_EXPORT uint qHash(const QRhiShaderResourceBinding &, uint) Q_DECL_NOTHROW;
+#ifndef QT_NO_DEBUG_STREAM
+ friend Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiShaderResourceBinding &);
+#endif
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QRhiShaderResourceBinding::StageFlags)
+
+Q_GUI_EXPORT bool operator==(const QRhiShaderResourceBinding &a, const QRhiShaderResourceBinding &b) Q_DECL_NOTHROW;
+Q_GUI_EXPORT bool operator!=(const QRhiShaderResourceBinding &a, const QRhiShaderResourceBinding &b) Q_DECL_NOTHROW;
+Q_GUI_EXPORT uint qHash(const QRhiShaderResourceBinding &b, uint seed = 0) Q_DECL_NOTHROW;
+#ifndef QT_NO_DEBUG_STREAM
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiShaderResourceBinding &);
+#endif
+
+class Q_GUI_EXPORT QRhiColorAttachment
+{
+public:
+ QRhiColorAttachment() = default;
+ QRhiColorAttachment(QRhiTexture *texture);
+ QRhiColorAttachment(QRhiRenderBuffer *renderBuffer);
+
+ QRhiTexture *texture() const { return m_texture; }
+ void setTexture(QRhiTexture *tex) { m_texture = tex; }
+
+ QRhiRenderBuffer *renderBuffer() const { return m_renderBuffer; }
+ void setRenderBuffer(QRhiRenderBuffer *rb) { m_renderBuffer = rb; }
+
+ int layer() const { return m_layer; }
+ void setLayer(int layer) { m_layer = layer; }
+
+ int level() const { return m_level; }
+ void setLevel(int level) { m_level = level; }
+
+ QRhiTexture *resolveTexture() const { return m_resolveTexture; }
+ void setResolveTexture(QRhiTexture *tex) { m_resolveTexture = tex; }
+
+ int resolveLayer() const { return m_resolveLayer; }
+ void setResolveLayer(int layer) { m_resolveLayer = layer; }
+
+ int resolveLevel() const { return m_resolveLevel; }
+ void setResolveLevel(int level) { m_resolveLevel = level; }
+
+private:
+ QRhiTexture *m_texture = nullptr;
+ QRhiRenderBuffer *m_renderBuffer = nullptr;
+ int m_layer = 0;
+ int m_level = 0;
+ QRhiTexture *m_resolveTexture = nullptr;
+ int m_resolveLayer = 0;
+ int m_resolveLevel = 0;
+};
+
+Q_DECLARE_TYPEINFO(QRhiColorAttachment, Q_MOVABLE_TYPE);
+
+class Q_GUI_EXPORT QRhiTextureRenderTargetDescription
+{
+public:
+ QRhiTextureRenderTargetDescription() = default;
+ QRhiTextureRenderTargetDescription(const QRhiColorAttachment &colorAttachment);
+ QRhiTextureRenderTargetDescription(const QRhiColorAttachment &colorAttachment, QRhiRenderBuffer *depthStencilBuffer);
+ QRhiTextureRenderTargetDescription(const QRhiColorAttachment &colorAttachment, QRhiTexture *depthTexture);
+
+ QVector<QRhiColorAttachment> colorAttachments() const { return m_colorAttachments; }
+ void setColorAttachments(const QVector<QRhiColorAttachment> &att) { m_colorAttachments = att; }
+
+ QRhiRenderBuffer *depthStencilBuffer() const { return m_depthStencilBuffer; }
+ void setDepthStencilBuffer(QRhiRenderBuffer *renderBuffer) { m_depthStencilBuffer = renderBuffer; }
+
+ QRhiTexture *depthTexture() const { return m_depthTexture; }
+ void setDepthTexture(QRhiTexture *texture) { m_depthTexture = texture; }
+
+private:
+ QVector<QRhiColorAttachment> m_colorAttachments;
+ QRhiRenderBuffer *m_depthStencilBuffer = nullptr;
+ QRhiTexture *m_depthTexture = nullptr;
+};
+
+Q_DECLARE_TYPEINFO(QRhiTextureRenderTargetDescription, Q_MOVABLE_TYPE);
+
+class Q_GUI_EXPORT QRhiTextureSubresourceUploadDescription
+{
+public:
+ QRhiTextureSubresourceUploadDescription() = default;
+ QRhiTextureSubresourceUploadDescription(const QImage &image);
+ QRhiTextureSubresourceUploadDescription(const void *data, int size);
+
+ QImage image() const { return m_image; }
+ void setImage(const QImage &image) { m_image = image; }
+
+ QByteArray data() const { return m_data; }
+ void setData(const QByteArray &data) { m_data = data; }
+
+ QPoint destinationTopLeft() const { return m_destinationTopLeft; }
+ void setDestinationTopLeft(const QPoint &p) { m_destinationTopLeft = p; }
+
+ QSize sourceSize() const { return m_sourceSize; }
+ void setSourceSize(const QSize &size) { m_sourceSize = size; }
+
+ QPoint sourceTopLeft() const { return m_sourceTopLeft; }
+ void setSourceTopLeft(const QPoint &p) { m_sourceTopLeft = p; }
+
+private:
+ QImage m_image;
+ QByteArray m_data;
+ QPoint m_destinationTopLeft;
+ QSize m_sourceSize;
+ QPoint m_sourceTopLeft;
+};
+
+Q_DECLARE_TYPEINFO(QRhiTextureSubresourceUploadDescription, Q_MOVABLE_TYPE);
+
+class Q_GUI_EXPORT QRhiTextureUploadEntry
+{
+public:
+ QRhiTextureUploadEntry() = default;
+ QRhiTextureUploadEntry(int layer, int level, const QRhiTextureSubresourceUploadDescription &desc);
+
+ int layer() const { return m_layer; }
+ void setLayer(int layer) { m_layer = layer; }
+
+ int level() const { return m_level; }
+ void setLevel(int level) { m_level = level; }
+
+ QRhiTextureSubresourceUploadDescription description() const { return m_desc; }
+ void setDescription(const QRhiTextureSubresourceUploadDescription &desc) { m_desc = desc; }
+
+private:
+ int m_layer = 0;
+ int m_level = 0;
+ QRhiTextureSubresourceUploadDescription m_desc;
+};
+
+Q_DECLARE_TYPEINFO(QRhiTextureUploadEntry, Q_MOVABLE_TYPE);
+
+class Q_GUI_EXPORT QRhiTextureUploadDescription
+{
+public:
+ QRhiTextureUploadDescription() = default;
+ QRhiTextureUploadDescription(const QRhiTextureUploadEntry &entry);
+ QRhiTextureUploadDescription(const QVector<QRhiTextureUploadEntry> &entries);
+
+ QVector<QRhiTextureUploadEntry> entries() const { return m_entries; }
+ void setEntries(const QVector<QRhiTextureUploadEntry> &entries) { m_entries = entries; }
+ void append(const QRhiTextureUploadEntry &entry);
+
+private:
+ QVector<QRhiTextureUploadEntry> m_entries;
+};
+
+Q_DECLARE_TYPEINFO(QRhiTextureUploadDescription, Q_MOVABLE_TYPE);
+
+class Q_GUI_EXPORT QRhiTextureCopyDescription
+{
+public:
+ QRhiTextureCopyDescription() = default;
+
+ QSize pixelSize() const { return m_pixelSize; }
+ void setPixelSize(const QSize &sz) { m_pixelSize = sz; }
+
+ int sourceLayer() const { return m_sourceLayer; }
+ void setSourceLayer(int layer) { m_sourceLayer = layer; }
+
+ int sourceLevel() const { return m_sourceLevel; }
+ void setSourceLevel(int level) { m_sourceLevel = level; }
+
+ QPoint sourceTopLeft() const { return m_sourceTopLeft; }
+ void setSourceTopLeft(const QPoint &p) { m_sourceTopLeft = p; }
+
+ int destinationLayer() const { return m_destinationLayer; }
+ void setDestinationLayer(int layer) { m_destinationLayer = layer; }
+
+ int destinationLevel() const { return m_destinationLevel; }
+ void setDestinationLevel(int level) { m_destinationLevel = level; }
+
+ QPoint destinationTopLeft() const { return m_destinationTopLeft; }
+ void setDestinationTopLeft(const QPoint &p) { m_destinationTopLeft = p; }
+
+private:
+ QSize m_pixelSize;
+ int m_sourceLayer = 0;
+ int m_sourceLevel = 0;
+ QPoint m_sourceTopLeft;
+ int m_destinationLayer = 0;
+ int m_destinationLevel = 0;
+ QPoint m_destinationTopLeft;
+};
+
+Q_DECLARE_TYPEINFO(QRhiTextureCopyDescription, Q_MOVABLE_TYPE);
+
+class Q_GUI_EXPORT QRhiReadbackDescription
+{
+public:
+ QRhiReadbackDescription() = default;
+ QRhiReadbackDescription(QRhiTexture *texture);
+
+ QRhiTexture *texture() const { return m_texture; }
+ void setTexture(QRhiTexture *tex) { m_texture = tex; }
+
+ int layer() const { return m_layer; }
+ void setLayer(int layer) { m_layer = layer; }
+
+ int level() const { return m_level; }
+ void setLevel(int level) { m_level = level; }
+
+private:
+ QRhiTexture *m_texture = nullptr;
+ int m_layer = 0;
+ int m_level = 0;
+};
+
+Q_DECLARE_TYPEINFO(QRhiReadbackDescription, Q_MOVABLE_TYPE);
+
+struct Q_GUI_EXPORT QRhiNativeHandles
+{
+};
+
+class Q_GUI_EXPORT QRhiResource
+{
+public:
+ enum Type {
+ Buffer,
+ Texture,
+ Sampler,
+ RenderBuffer,
+ RenderPassDescriptor,
+ RenderTarget,
+ TextureRenderTarget,
+ ShaderResourceBindings,
+ GraphicsPipeline,
+ SwapChain,
+ CommandBuffer
+ };
+
+ virtual ~QRhiResource();
+
+ virtual Type resourceType() const = 0;
+
+ virtual void release() = 0;
+ void releaseAndDestroyLater();
+
+ QByteArray name() const;
+ void setName(const QByteArray &name);
+
+ quint64 globalResourceId() const;
+
+protected:
+ QRhiResource(QRhiImplementation *rhi);
+ Q_DISABLE_COPY(QRhiResource)
+ friend class QRhiImplementation;
+ QRhiImplementation *m_rhi = nullptr;
+ quint64 m_id;
+ QByteArray m_objectName;
+};
+
+class Q_GUI_EXPORT QRhiBuffer : public QRhiResource
+{
+public:
+ enum Type {
+ Immutable,
+ Static,
+ Dynamic
+ };
+
+ enum UsageFlag {
+ VertexBuffer = 1 << 0,
+ IndexBuffer = 1 << 1,
+ UniformBuffer = 1 << 2
+ };
+ Q_DECLARE_FLAGS(UsageFlags, UsageFlag)
+
+ QRhiResource::Type resourceType() const override;
+
+ Type type() const { return m_type; }
+ void setType(Type t) { m_type = t; }
+
+ UsageFlags usage() const { return m_usage; }
+ void setUsage(UsageFlags u) { m_usage = u; }
+
+ int size() const { return m_size; }
+ void setSize(int sz) { m_size = sz; }
+
+ virtual bool build() = 0;
+
+protected:
+ QRhiBuffer(QRhiImplementation *rhi, Type type_, UsageFlags usage_, int size_);
+ Type m_type;
+ UsageFlags m_usage;
+ int m_size;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QRhiBuffer::UsageFlags)
+
+class Q_GUI_EXPORT QRhiTexture : public QRhiResource
+{
+public:
+ enum Flag {
+ RenderTarget = 1 << 0,
+ CubeMap = 1 << 2,
+ MipMapped = 1 << 3,
+ sRGB = 1 << 4,
+ UsedAsTransferSource = 1 << 5,
+ UsedWithGenerateMips = 1 << 6
+ };
+ Q_DECLARE_FLAGS(Flags, Flag)
+
+ enum Format {
+ UnknownFormat,
+
+ RGBA8,
+ BGRA8,
+ R8,
+ R16,
+ RED_OR_ALPHA8,
+
+ RGBA16F,
+ RGBA32F,
+
+ D16,
+ D32F,
+
+ BC1,
+ BC2,
+ BC3,
+ BC4,
+ BC5,
+ BC6H,
+ BC7,
+
+ ETC2_RGB8,
+ ETC2_RGB8A1,
+ ETC2_RGBA8,
+
+ ASTC_4x4,
+ ASTC_5x4,
+ ASTC_5x5,
+ ASTC_6x5,
+ ASTC_6x6,
+ ASTC_8x5,
+ ASTC_8x6,
+ ASTC_8x8,
+ ASTC_10x5,
+ ASTC_10x6,
+ ASTC_10x8,
+ ASTC_10x10,
+ ASTC_12x10,
+ ASTC_12x12
+ };
+
+ QRhiResource::Type resourceType() const override;
+
+ Format format() const { return m_format; }
+ void setFormat(Format fmt) { m_format = fmt; }
+
+ QSize pixelSize() const { return m_pixelSize; }
+ void setPixelSize(const QSize &sz) { m_pixelSize = sz; }
+
+ Flags flags() const { return m_flags; }
+ void setFlags(Flags f) { m_flags = f; }
+
+ int sampleCount() const { return m_sampleCount; }
+ void setSampleCount(int s) { m_sampleCount = s; }
+
+ virtual bool build() = 0;
+ virtual const QRhiNativeHandles *nativeHandles();
+ virtual bool buildFrom(const QRhiNativeHandles *src);
+
+protected:
+ QRhiTexture(QRhiImplementation *rhi, Format format_, const QSize &pixelSize_,
+ int sampleCount_, Flags flags_);
+ Format m_format;
+ QSize m_pixelSize;
+ int m_sampleCount;
+ Flags m_flags;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QRhiTexture::Flags)
+
+class Q_GUI_EXPORT QRhiSampler : public QRhiResource
+{
+public:
+ enum Filter {
+ None,
+ Nearest,
+ Linear
+ };
+
+ enum AddressMode {
+ Repeat,
+ ClampToEdge,
+ Border,
+ Mirror,
+ MirrorOnce
+ };
+
+ enum CompareOp {
+ Never,
+ Less,
+ Equal,
+ LessOrEqual,
+ Greater,
+ NotEqual,
+ GreaterOrEqual,
+ Always
+ };
+
+ QRhiResource::Type resourceType() const override;
+
+ Filter magFilter() const { return m_magFilter; }
+ void setMagFilter(Filter f) { m_magFilter = f; }
+
+ Filter minFilter() const { return m_minFilter; }
+ void setMinFilter(Filter f) { m_minFilter = f; }
+
+ Filter mipmapMode() const { return m_mipmapMode; }
+ void setMipmapMode(Filter f) { m_mipmapMode = f; }
+
+ AddressMode addressU() const { return m_addressU; }
+ void setAddressU(AddressMode mode) { m_addressU = mode; }
+
+ AddressMode addressV() const { return m_addressV; }
+ void setAddressV(AddressMode mode) { m_addressV = mode; }
+
+ AddressMode addressW() const { return m_addressW; }
+ void setAddressW(AddressMode mode) { m_addressW = mode; }
+
+ CompareOp textureCompareOp() const { return m_compareOp; }
+ void setTextureCompareOp(CompareOp op) { m_compareOp = op; }
+
+ virtual bool build() = 0;
+
+protected:
+ QRhiSampler(QRhiImplementation *rhi,
+ Filter magFilter_, Filter minFilter_, Filter mipmapMode_,
+ AddressMode u_, AddressMode v_);
+ Filter m_magFilter;
+ Filter m_minFilter;
+ Filter m_mipmapMode;
+ AddressMode m_addressU;
+ AddressMode m_addressV;
+ AddressMode m_addressW;
+ CompareOp m_compareOp;
+};
+
+class Q_GUI_EXPORT QRhiRenderBuffer : public QRhiResource
+{
+public:
+ enum Type {
+ DepthStencil,
+ Color
+ };
+
+ enum Flag {
+ UsedWithSwapChainOnly = 1 << 0
+ };
+ Q_DECLARE_FLAGS(Flags, Flag)
+
+ QRhiResource::Type resourceType() const override;
+
+ Type type() const { return m_type; }
+ void setType(Type t) { m_type = t; }
+
+ QSize pixelSize() const { return m_pixelSize; }
+ void setPixelSize(const QSize &sz) { m_pixelSize = sz; }
+
+ int sampleCount() const { return m_sampleCount; }
+ void setSampleCount(int s) { m_sampleCount = s; }
+
+ Flags flags() const { return m_flags; }
+ void setFlags(Flags h) { m_flags = h; }
+
+ virtual bool build() = 0;
+
+ virtual QRhiTexture::Format backingFormat() const = 0;
+
+protected:
+ QRhiRenderBuffer(QRhiImplementation *rhi, Type type_, const QSize &pixelSize_,
+ int sampleCount_, Flags flags_);
+ Type m_type;
+ QSize m_pixelSize;
+ int m_sampleCount;
+ Flags m_flags;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QRhiRenderBuffer::Flags)
+
+class Q_GUI_EXPORT QRhiRenderPassDescriptor : public QRhiResource
+{
+public:
+ QRhiResource::Type resourceType() const override;
+
+protected:
+ QRhiRenderPassDescriptor(QRhiImplementation *rhi);
+};
+
+class Q_GUI_EXPORT QRhiRenderTarget : public QRhiResource
+{
+public:
+ QRhiResource::Type resourceType() const override;
+
+ virtual QSize pixelSize() const = 0;
+ virtual float devicePixelRatio() const = 0;
+ virtual int sampleCount() const = 0;
+
+ QRhiRenderPassDescriptor *renderPassDescriptor() const { return m_renderPassDesc; }
+ void setRenderPassDescriptor(QRhiRenderPassDescriptor *desc) { m_renderPassDesc = desc; }
+
+protected:
+ QRhiRenderTarget(QRhiImplementation *rhi);
+ QRhiRenderPassDescriptor *m_renderPassDesc = nullptr;
+};
+
+class Q_GUI_EXPORT QRhiTextureRenderTarget : public QRhiRenderTarget
+{
+public:
+ enum Flag {
+ PreserveColorContents = 1 << 0,
+ PreserveDepthStencilContents = 1 << 1
+ };
+ Q_DECLARE_FLAGS(Flags, Flag)
+
+ QRhiResource::Type resourceType() const override;
+
+ QRhiTextureRenderTargetDescription description() const { return m_desc; }
+ void setDescription(const QRhiTextureRenderTargetDescription &desc) { m_desc = desc; }
+
+ Flags flags() const { return m_flags; }
+ void setFlags(Flags f) { m_flags = f; }
+
+ virtual QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() = 0;
+
+ virtual bool build() = 0;
+
+protected:
+ QRhiTextureRenderTarget(QRhiImplementation *rhi, const QRhiTextureRenderTargetDescription &desc_, Flags flags_);
+ QRhiTextureRenderTargetDescription m_desc;
+ Flags m_flags;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QRhiTextureRenderTarget::Flags)
+
+class Q_GUI_EXPORT QRhiShaderResourceBindings : public QRhiResource
+{
+public:
+ QRhiResource::Type resourceType() const override;
+
+ QVector<QRhiShaderResourceBinding> bindings() const { return m_bindings; }
+ void setBindings(const QVector<QRhiShaderResourceBinding> &b) { m_bindings = b; }
+
+ bool isLayoutCompatible(const QRhiShaderResourceBindings *other) const;
+
+ virtual bool build() = 0;
+
+protected:
+ QRhiShaderResourceBindings(QRhiImplementation *rhi);
+ QVector<QRhiShaderResourceBinding> m_bindings;
+#ifndef QT_NO_DEBUG_STREAM
+ friend Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiShaderResourceBindings &);
+#endif
+};
+
+#ifndef QT_NO_DEBUG_STREAM
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiShaderResourceBindings &);
+#endif
+
+class Q_GUI_EXPORT QRhiGraphicsPipeline : public QRhiResource
+{
+public:
+ enum Flag {
+ UsesBlendConstants = 1 << 0,
+ UsesStencilRef = 1 << 1,
+ UsesScissor = 1 << 2
+ };
+ Q_DECLARE_FLAGS(Flags, Flag)
+
+ enum Topology {
+ Triangles,
+ TriangleStrip,
+ Lines,
+ LineStrip,
+ Points
+ };
+
+ enum CullMode {
+ None,
+ Front,
+ Back
+ };
+
+ enum FrontFace {
+ CCW,
+ CW
+ };
+
+ enum ColorMaskComponent {
+ R = 1 << 0,
+ G = 1 << 1,
+ B = 1 << 2,
+ A = 1 << 3
+ };
+ Q_DECLARE_FLAGS(ColorMask, ColorMaskComponent)
+
+ enum BlendFactor {
+ Zero,
+ One,
+ SrcColor,
+ OneMinusSrcColor,
+ DstColor,
+ OneMinusDstColor,
+ SrcAlpha,
+ OneMinusSrcAlpha,
+ DstAlpha,
+ OneMinusDstAlpha,
+ ConstantColor,
+ OneMinusConstantColor,
+ ConstantAlpha,
+ OneMinusConstantAlpha,
+ SrcAlphaSaturate,
+ Src1Color,
+ OneMinusSrc1Color,
+ Src1Alpha,
+ OneMinusSrc1Alpha
+ };
+
+ enum BlendOp {
+ Add,
+ Subtract,
+ ReverseSubtract,
+ Min,
+ Max
+ };
+
+ struct TargetBlend {
+ ColorMask colorWrite = ColorMask(0xF); // R | G | B | A
+ bool enable = false;
+ BlendFactor srcColor = One;
+ BlendFactor dstColor = OneMinusSrcAlpha;
+ BlendOp opColor = Add;
+ BlendFactor srcAlpha = One;
+ BlendFactor dstAlpha = OneMinusSrcAlpha;
+ BlendOp opAlpha = Add;
+ };
+
+ enum CompareOp {
+ Never,
+ Less,
+ Equal,
+ LessOrEqual,
+ Greater,
+ NotEqual,
+ GreaterOrEqual,
+ Always
+ };
+
+ enum StencilOp {
+ StencilZero,
+ Keep,
+ Replace,
+ IncrementAndClamp,
+ DecrementAndClamp,
+ Invert,
+ IncrementAndWrap,
+ DecrementAndWrap
+ };
+
+ struct StencilOpState {
+ StencilOp failOp = Keep;
+ StencilOp depthFailOp = Keep;
+ StencilOp passOp = Keep;
+ CompareOp compareOp = Always;
+ };
+
+ QRhiResource::Type resourceType() const override;
+
+ Flags flags() const { return m_flags; }
+ void setFlags(Flags f) { m_flags = f; }
+
+ Topology topology() const { return m_topology; }
+ void setTopology(Topology t) { m_topology = t; }
+
+ CullMode cullMode() const { return m_cullMode; }
+ void setCullMode(CullMode mode) { m_cullMode = mode; }
+
+ FrontFace frontFace() const { return m_frontFace; }
+ void setFrontFace(FrontFace f) { m_frontFace = f; }
+
+ QVector<TargetBlend> targetBlends() const { return m_targetBlends; }
+ void setTargetBlends(const QVector<TargetBlend> &blends) { m_targetBlends = blends; }
+
+ bool hasDepthTest() const { return m_depthTest; }
+ void setDepthTest(bool enable) { m_depthTest = enable; }
+
+ bool hasDepthWrite() const { return m_depthWrite; }
+ void setDepthWrite(bool enable) { m_depthWrite = enable; }
+
+ CompareOp depthOp() const { return m_depthOp; }
+ void setDepthOp(CompareOp op) { m_depthOp = op; }
+
+ bool hasStencilTest() const { return m_stencilTest; }
+ void setStencilTest(bool enable) { m_stencilTest = enable; }
+
+ StencilOpState stencilFront() const { return m_stencilFront; }
+ void setStencilFront(const StencilOpState &state) { m_stencilFront = state; }
+
+ StencilOpState stencilBack() const { return m_stencilBack; }
+ void setStencilBack(const StencilOpState &state) { m_stencilBack = state; }
+
+ quint32 stencilReadMask() const { return m_stencilReadMask; }
+ void setStencilReadMask(quint32 mask) { m_stencilReadMask = mask; }
+
+ quint32 stencilWriteMask() const { return m_stencilWriteMask; }
+ void setStencilWriteMask(quint32 mask) { m_stencilWriteMask = mask; }
+
+ int sampleCount() const { return m_sampleCount; }
+ void setSampleCount(int s) { m_sampleCount = s; }
+
+ QVector<QRhiGraphicsShaderStage> shaderStages() const { return m_shaderStages; }
+ void setShaderStages(const QVector<QRhiGraphicsShaderStage> &stages) { m_shaderStages = stages; }
+
+ QRhiVertexInputLayout vertexInputLayout() const { return m_vertexInputLayout; }
+ void setVertexInputLayout(const QRhiVertexInputLayout &layout) { m_vertexInputLayout = layout; }
+
+ QRhiShaderResourceBindings *shaderResourceBindings() const { return m_shaderResourceBindings; }
+ void setShaderResourceBindings(QRhiShaderResourceBindings *srb) { m_shaderResourceBindings = srb; }
+
+ QRhiRenderPassDescriptor *renderPassDescriptor() const { return m_renderPassDesc; }
+ void setRenderPassDescriptor(QRhiRenderPassDescriptor *desc) { m_renderPassDesc = desc; }
+
+ virtual bool build() = 0;
+
+protected:
+ QRhiGraphicsPipeline(QRhiImplementation *rhi);
+ Flags m_flags;
+ Topology m_topology = Triangles;
+ CullMode m_cullMode = None;
+ FrontFace m_frontFace = CCW;
+ QVector<TargetBlend> m_targetBlends;
+ bool m_depthTest = false;
+ bool m_depthWrite = false;
+ CompareOp m_depthOp = Less;
+ bool m_stencilTest = false;
+ StencilOpState m_stencilFront;
+ StencilOpState m_stencilBack;
+ quint32 m_stencilReadMask = 0xFF;
+ quint32 m_stencilWriteMask = 0xFF;
+ int m_sampleCount = 1;
+ QVector<QRhiGraphicsShaderStage> m_shaderStages;
+ QRhiVertexInputLayout m_vertexInputLayout;
+ QRhiShaderResourceBindings *m_shaderResourceBindings = nullptr;
+ QRhiRenderPassDescriptor *m_renderPassDesc = nullptr;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QRhiGraphicsPipeline::Flags)
+Q_DECLARE_OPERATORS_FOR_FLAGS(QRhiGraphicsPipeline::ColorMask)
+Q_DECLARE_TYPEINFO(QRhiGraphicsPipeline::TargetBlend, Q_MOVABLE_TYPE);
+
+class Q_GUI_EXPORT QRhiSwapChain : public QRhiResource
+{
+public:
+ enum Flag {
+ SurfaceHasPreMulAlpha = 1 << 0,
+ SurfaceHasNonPreMulAlpha = 1 << 1,
+ sRGB = 1 << 2,
+ UsedAsTransferSource = 1 << 3,
+ NoVSync = 1 << 4,
+ MinimalBufferCount = 1 << 5
+ };
+ Q_DECLARE_FLAGS(Flags, Flag)
+
+ QRhiResource::Type resourceType() const override;
+
+ QWindow *window() const { return m_window; }
+ void setWindow(QWindow *window) { m_window = window; }
+
+ Flags flags() const { return m_flags; }
+ void setFlags(Flags f) { m_flags = f; }
+
+ QRhiRenderBuffer *depthStencil() const { return m_depthStencil; }
+ void setDepthStencil(QRhiRenderBuffer *ds) { m_depthStencil = ds; }
+
+ int sampleCount() const { return m_sampleCount; }
+ void setSampleCount(int samples) { m_sampleCount = samples; }
+
+ QRhiRenderPassDescriptor *renderPassDescriptor() const { return m_renderPassDesc; }
+ void setRenderPassDescriptor(QRhiRenderPassDescriptor *desc) { m_renderPassDesc = desc; }
+
+ QSize currentPixelSize() const { return m_currentPixelSize; }
+
+ virtual QRhiCommandBuffer *currentFrameCommandBuffer() = 0;
+ virtual QRhiRenderTarget *currentFrameRenderTarget() = 0;
+ virtual QSize surfacePixelSize() = 0;
+ virtual QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() = 0;
+ virtual bool buildOrResize() = 0;
+
+protected:
+ QRhiSwapChain(QRhiImplementation *rhi);
+ QWindow *m_window = nullptr;
+ Flags m_flags;
+ QRhiRenderBuffer *m_depthStencil = nullptr;
+ int m_sampleCount = 1;
+ QRhiRenderPassDescriptor *m_renderPassDesc = nullptr;
+ QSize m_currentPixelSize;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QRhiSwapChain::Flags)
+
+class Q_GUI_EXPORT QRhiCommandBuffer : public QRhiResource
+{
+public:
+ enum IndexFormat {
+ IndexUInt16,
+ IndexUInt32
+ };
+
+ QRhiResource::Type resourceType() const override;
+
+ void resourceUpdate(QRhiResourceUpdateBatch *resourceUpdates);
+
+ void beginPass(QRhiRenderTarget *rt,
+ const QColor &colorClearValue,
+ const QRhiDepthStencilClearValue &depthStencilClearValue,
+ QRhiResourceUpdateBatch *resourceUpdates = nullptr);
+ void endPass(QRhiResourceUpdateBatch *resourceUpdates = nullptr);
+
+ void setGraphicsPipeline(QRhiGraphicsPipeline *ps);
+ using DynamicOffset = QPair<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);
+
+ const QRhiNativeHandles *nativeHandles();
+ void beginExternal();
+ void endExternal();
+
+protected:
+ QRhiCommandBuffer(QRhiImplementation *rhi);
+};
+
+struct Q_GUI_EXPORT QRhiReadbackResult
+{
+ std::function<void()> completed = nullptr;
+ QRhiTexture::Format format;
+ QSize pixelSize;
+ QByteArray data;
+}; // non-movable due to the std::function
+
+class Q_GUI_EXPORT QRhiResourceUpdateBatch
+{
+public:
+ ~QRhiResourceUpdateBatch();
+
+ void release();
+
+ void merge(QRhiResourceUpdateBatch *other);
+
+ void updateDynamicBuffer(QRhiBuffer *buf, int offset, int size, const void *data);
+ void uploadStaticBuffer(QRhiBuffer *buf, int offset, int size, const void *data);
+ void uploadStaticBuffer(QRhiBuffer *buf, const void *data);
+ void uploadTexture(QRhiTexture *tex, const QRhiTextureUploadDescription &desc);
+ void uploadTexture(QRhiTexture *tex, const QImage &image);
+ void copyTexture(QRhiTexture *dst, QRhiTexture *src, const QRhiTextureCopyDescription &desc = QRhiTextureCopyDescription());
+ void readBackTexture(const QRhiReadbackDescription &rb, QRhiReadbackResult *result);
+ void generateMips(QRhiTexture *tex, int layer = 0);
+
+private:
+ QRhiResourceUpdateBatch(QRhiImplementation *rhi);
+ Q_DISABLE_COPY(QRhiResourceUpdateBatch)
+ QRhiResourceUpdateBatchPrivate *d;
+ friend class QRhiResourceUpdateBatchPrivate;
+ friend class QRhi;
+};
+
+struct Q_GUI_EXPORT QRhiInitParams
+{
+};
+
+class Q_GUI_EXPORT QRhi
+{
+public:
+ enum Implementation {
+ Null,
+ Vulkan,
+ OpenGLES2,
+ D3D11,
+ Metal
+ };
+
+ enum Flag {
+ EnableProfiling = 1 << 0,
+ EnableDebugMarkers = 1 << 1
+ };
+ Q_DECLARE_FLAGS(Flags, Flag)
+
+ enum FrameOpResult {
+ FrameOpSuccess = 0,
+ FrameOpError,
+ FrameOpSwapChainOutOfDate,
+ FrameOpDeviceLost
+ };
+
+ enum Feature {
+ MultisampleTexture = 1,
+ MultisampleRenderBuffer,
+ DebugMarkers,
+ Timestamps,
+ Instancing,
+ CustomInstanceStepRate,
+ PrimitiveRestart,
+ NonDynamicUniformBuffers,
+ NonFourAlignedEffectiveIndexBufferOffset,
+ NPOTTextureRepeat,
+ RedOrAlpha8IsRed,
+ ElementIndexUint
+ };
+
+ enum BeginFrameFlag {
+ };
+ Q_DECLARE_FLAGS(BeginFrameFlags, BeginFrameFlag)
+
+ enum EndFrameFlag {
+ SkipPresent = 1 << 0
+ };
+ Q_DECLARE_FLAGS(EndFrameFlags, EndFrameFlag)
+
+ enum ResourceLimit {
+ TextureSizeMin = 1,
+ TextureSizeMax,
+ MaxColorAttachments,
+ FramesInFlight
+ };
+
+ ~QRhi();
+
+ static QRhi *create(Implementation impl,
+ QRhiInitParams *params,
+ Flags flags = Flags(),
+ QRhiNativeHandles *importDevice = nullptr);
+
+ Implementation backend() const;
+ QThread *thread() const;
+
+ using CleanupCallback = std::function<void(QRhi *)>;
+ void addCleanupCallback(const CleanupCallback &callback);
+ void runCleanup();
+
+ QRhiGraphicsPipeline *newGraphicsPipeline();
+ QRhiShaderResourceBindings *newShaderResourceBindings();
+
+ QRhiBuffer *newBuffer(QRhiBuffer::Type type,
+ QRhiBuffer::UsageFlags usage,
+ int size);
+
+ QRhiRenderBuffer *newRenderBuffer(QRhiRenderBuffer::Type type,
+ const QSize &pixelSize,
+ int sampleCount = 1,
+ QRhiRenderBuffer::Flags flags = QRhiRenderBuffer::Flags());
+
+ QRhiTexture *newTexture(QRhiTexture::Format format,
+ const QSize &pixelSize,
+ int sampleCount = 1,
+ QRhiTexture::Flags flags = QRhiTexture::Flags());
+
+ QRhiSampler *newSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
+ QRhiSampler::Filter mipmapMode,
+ QRhiSampler::AddressMode u, QRhiSampler::AddressMode v);
+
+ QRhiTextureRenderTarget *newTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
+ QRhiTextureRenderTarget::Flags flags = QRhiTextureRenderTarget::Flags());
+
+ QRhiSwapChain *newSwapChain();
+ FrameOpResult beginFrame(QRhiSwapChain *swapChain, BeginFrameFlags flags = BeginFrameFlags());
+ FrameOpResult endFrame(QRhiSwapChain *swapChain, EndFrameFlags flags = EndFrameFlags());
+ bool isRecordingFrame() const;
+ int currentFrameSlot() const;
+
+ FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb);
+ FrameOpResult endOffscreenFrame();
+
+ QRhi::FrameOpResult finish();
+
+ QRhiResourceUpdateBatch *nextResourceUpdateBatch();
+
+ QVector<int> supportedSampleCounts() const;
+
+ int ubufAlignment() const;
+ int ubufAligned(int v) const;
+
+ int mipLevelsForSize(const QSize &size) const;
+ QSize sizeForMipLevel(int mipLevel, const QSize &baseLevelSize) const;
+
+ bool isYUpInFramebuffer() const;
+ bool isYUpInNDC() const;
+ bool isClipDepthZeroToOne() const;
+
+ QMatrix4x4 clipSpaceCorrMatrix() const;
+
+ bool isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags = QRhiTexture::Flags()) const;
+ bool isFeatureSupported(QRhi::Feature feature) const;
+ int resourceLimit(ResourceLimit limit) const;
+
+ const QRhiNativeHandles *nativeHandles();
+
+ QRhiProfiler *profiler();
+
+ static const int MAX_LAYERS = 6; // cubemaps only
+ static const int MAX_LEVELS = 16; // a width and/or height of 65536 should be enough for everyone
+
+protected:
+ QRhi();
+
+private:
+ Q_DISABLE_COPY(QRhi)
+ QRhiImplementation *d = nullptr;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QRhi::Flags)
+Q_DECLARE_OPERATORS_FOR_FLAGS(QRhi::BeginFrameFlags)
+Q_DECLARE_OPERATORS_FOR_FLAGS(QRhi::EndFrameFlags)
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/rhi/qrhi_p_p.h b/src/gui/rhi/qrhi_p_p.h
new file mode 100644
index 0000000000..7c110431fb
--- /dev/null
+++ b/src/gui/rhi/qrhi_p_p.h
@@ -0,0 +1,544 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gui module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QRHI_P_H
+#define QRHI_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qrhi_p.h"
+#include "qrhiprofiler_p_p.h"
+#include <QBitArray>
+#include <QAtomicInt>
+#include <QAtomicInteger>
+
+QT_BEGIN_NAMESPACE
+
+#define QRHI_RES(t, x) static_cast<t *>(x)
+#define QRHI_RES_RHI(t) t *rhiD = static_cast<t *>(m_rhi)
+#define QRHI_PROF QRhiProfilerPrivate *rhiP = m_rhi->profilerPrivateOrNull()
+#define QRHI_PROF_F(f) for (bool qrhip_enabled = rhiP != nullptr; qrhip_enabled; qrhip_enabled = false) rhiP->f
+
+class QRhiImplementation
+{
+public:
+ virtual ~QRhiImplementation();
+
+ virtual bool create(QRhi::Flags flags) = 0;
+ virtual void destroy() = 0;
+
+ virtual QRhiGraphicsPipeline *createGraphicsPipeline() = 0;
+ virtual QRhiShaderResourceBindings *createShaderResourceBindings() = 0;
+ virtual QRhiBuffer *createBuffer(QRhiBuffer::Type type,
+ QRhiBuffer::UsageFlags usage,
+ int size) = 0;
+ virtual QRhiRenderBuffer *createRenderBuffer(QRhiRenderBuffer::Type type,
+ const QSize &pixelSize,
+ int sampleCount,
+ QRhiRenderBuffer::Flags flags) = 0;
+ virtual QRhiTexture *createTexture(QRhiTexture::Format format,
+ const QSize &pixelSize,
+ int sampleCount,
+ QRhiTexture::Flags flags) = 0;
+ virtual QRhiSampler *createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
+ QRhiSampler::Filter mipmapMode,
+ QRhiSampler:: AddressMode u, QRhiSampler::AddressMode v) = 0;
+
+ virtual QRhiTextureRenderTarget *createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
+ QRhiTextureRenderTarget::Flags flags) = 0;
+
+ virtual QRhiSwapChain *createSwapChain() = 0;
+ virtual QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) = 0;
+ virtual QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) = 0;
+ virtual QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb) = 0;
+ virtual QRhi::FrameOpResult endOffscreenFrame() = 0;
+ virtual QRhi::FrameOpResult finish() = 0;
+
+ virtual void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) = 0;
+
+ virtual void beginPass(QRhiCommandBuffer *cb,
+ QRhiRenderTarget *rt,
+ const QColor &colorClearValue,
+ const QRhiDepthStencilClearValue &depthStencilClearValue,
+ QRhiResourceUpdateBatch *resourceUpdates) = 0;
+ virtual void endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) = 0;
+
+ virtual void setGraphicsPipeline(QRhiCommandBuffer *cb,
+ QRhiGraphicsPipeline *ps) = 0;
+
+ virtual void setShaderResources(QRhiCommandBuffer *cb,
+ QRhiShaderResourceBindings *srb,
+ int dynamicOffsetCount,
+ const QRhiCommandBuffer::DynamicOffset *dynamicOffsets) = 0;
+
+ virtual void setVertexInput(QRhiCommandBuffer *cb,
+ int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings,
+ QRhiBuffer *indexBuf, quint32 indexOffset,
+ QRhiCommandBuffer::IndexFormat indexFormat) = 0;
+
+ virtual void setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport) = 0;
+ virtual void setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) = 0;
+ virtual void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) = 0;
+ virtual void setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) = 0;
+
+ virtual void draw(QRhiCommandBuffer *cb, quint32 vertexCount,
+ quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) = 0;
+ virtual void drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
+ quint32 instanceCount, quint32 firstIndex,
+ qint32 vertexOffset, quint32 firstInstance) = 0;
+
+ virtual void debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name) = 0;
+ virtual void debugMarkEnd(QRhiCommandBuffer *cb) = 0;
+ virtual void debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg) = 0;
+
+ virtual const QRhiNativeHandles *nativeHandles(QRhiCommandBuffer *cb) = 0;
+ virtual void beginExternal(QRhiCommandBuffer *cb) = 0;
+ virtual void endExternal(QRhiCommandBuffer *cb) = 0;
+
+ virtual QVector<int> supportedSampleCounts() const = 0;
+ virtual int ubufAlignment() const = 0;
+ virtual bool isYUpInFramebuffer() const = 0;
+ virtual bool isYUpInNDC() const = 0;
+ virtual bool isClipDepthZeroToOne() const = 0;
+ virtual QMatrix4x4 clipSpaceCorrMatrix() const = 0;
+ virtual bool isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const = 0;
+ virtual bool isFeatureSupported(QRhi::Feature feature) const = 0;
+ virtual int resourceLimit(QRhi::ResourceLimit limit) const = 0;
+ virtual const QRhiNativeHandles *nativeHandles() = 0;
+ virtual void sendVMemStatsToProfiler() = 0;
+
+ bool isCompressedFormat(QRhiTexture::Format format) const;
+ void compressedFormatInfo(QRhiTexture::Format format, const QSize &size,
+ quint32 *bpl, quint32 *byteSize,
+ QSize *blockDim) const;
+ void textureFormatInfo(QRhiTexture::Format format, const QSize &size,
+ quint32 *bpl, quint32 *byteSize) const;
+ quint32 approxByteSizeForTexture(QRhiTexture::Format format, const QSize &baseSize,
+ int mipCount, int layerCount);
+
+ QRhiProfilerPrivate *profilerPrivateOrNull()
+ {
+ // return null when QRhi::EnableProfiling was not set
+ QRhiProfilerPrivate *p = QRhiProfilerPrivate::get(&profiler);
+ return p->rhiDWhenEnabled ? p : nullptr;
+ }
+
+ // only really care about resources that own native graphics resources underneath
+ void registerResource(QRhiResource *res)
+ {
+ resources.insert(res);
+ }
+
+ void unregisterResource(QRhiResource *res)
+ {
+ resources.remove(res);
+ }
+
+ QSet<QRhiResource *> activeResources() const
+ {
+ return resources;
+ }
+
+ void addReleaseAndDestroyLater(QRhiResource *res)
+ {
+ if (inFrame)
+ pendingReleaseAndDestroyResources.insert(res);
+ else
+ delete res;
+ }
+
+ void addCleanupCallback(const QRhi::CleanupCallback &callback)
+ {
+ cleanupCallbacks.append(callback);
+ }
+
+ QRhi *q;
+
+protected:
+ bool debugMarkers = false;
+ int currentFrameSlot = 0; // for vk, mtl, and similar. unused by gl and d3d11.
+
+private:
+ QRhi::Implementation implType;
+ QThread *implThread;
+ QRhiProfiler profiler;
+ QVector<QRhiResourceUpdateBatch *> resUpdPool;
+ QBitArray resUpdPoolMap;
+ QSet<QRhiResource *> resources;
+ QSet<QRhiResource *> pendingReleaseAndDestroyResources;
+ QVector<QRhi::CleanupCallback> cleanupCallbacks;
+ bool inFrame = false;
+
+ friend class QRhi;
+ friend class QRhiResourceUpdateBatchPrivate;
+};
+
+template<typename T, size_t N>
+bool qrhi_toTopLeftRenderTargetRect(const QSize &outputSize, const std::array<T, N> &r,
+ T *x, T *y, T *w, T *h)
+{
+ // x,y are bottom-left in QRhiScissor and QRhiViewport but top-left in
+ // Vulkan/Metal/D3D. We also need proper clamping since some
+ // validation/debug layers are allergic to out of bounds scissor or
+ // viewport rects.
+
+ const T outputWidth = outputSize.width();
+ const T outputHeight = outputSize.height();
+ const T inputWidth = r[2];
+ const T inputHeight = r[3];
+
+ *x = qMax<T>(0, r[0]);
+ *y = qMax<T>(0, outputHeight - (r[1] + inputHeight));
+ *w = inputWidth;
+ *h = inputHeight;
+
+ if (*x >= outputWidth || *y >= outputHeight)
+ return false;
+
+ if (*x + *w > outputWidth)
+ *w = outputWidth - *x;
+ if (*y + *h > outputHeight)
+ *h = outputHeight - *y;
+
+ return true;
+}
+
+class QRhiResourceUpdateBatchPrivate
+{
+public:
+ struct DynamicBufferUpdate {
+ DynamicBufferUpdate() { }
+ DynamicBufferUpdate(QRhiBuffer *buf_, int offset_, int size_, const void *data_)
+ : buf(buf_), offset(offset_), data(reinterpret_cast<const char *>(data_), size_)
+ { }
+
+ QRhiBuffer *buf = nullptr;
+ int offset = 0;
+ QByteArray data;
+ };
+
+ struct StaticBufferUpload {
+ StaticBufferUpload() { }
+ StaticBufferUpload(QRhiBuffer *buf_, int offset_, int size_, const void *data_)
+ : buf(buf_), offset(offset_), data(reinterpret_cast<const char *>(data_), size_ ? size_ : buf_->size())
+ { }
+
+ QRhiBuffer *buf = nullptr;
+ int offset = 0;
+ QByteArray data;
+ };
+
+ struct TextureOp {
+ enum Type {
+ Upload,
+ Copy,
+ Read,
+ MipGen
+ };
+ Type type;
+ struct SUpload {
+ QRhiTexture *tex = nullptr;
+ // Specifying multiple uploads for a subresource must be supported.
+ // In the backend this can then end up, where applicable, as a
+ // single, batched copy operation with only one set of barriers.
+ // This helps when doing for example glyph cache fills.
+ QVector<QRhiTextureSubresourceUploadDescription> subresDesc[QRhi::MAX_LAYERS][QRhi::MAX_LEVELS];
+ } upload;
+ struct SCopy {
+ QRhiTexture *dst = nullptr;
+ QRhiTexture *src = nullptr;
+ QRhiTextureCopyDescription desc;
+ } copy;
+ struct SRead {
+ QRhiReadbackDescription rb;
+ QRhiReadbackResult *result;
+ } read;
+ struct SMipGen {
+ QRhiTexture *tex = nullptr;
+ int layer = 0;
+ } mipgen;
+
+ static TextureOp textureUpload(QRhiTexture *tex, const QRhiTextureUploadDescription &desc)
+ {
+ TextureOp op;
+ op.type = Upload;
+ op.upload.tex = tex;
+ const QVector<QRhiTextureUploadEntry> &entries(desc.entries());
+ for (const QRhiTextureUploadEntry &entry : entries)
+ op.upload.subresDesc[entry.layer()][entry.level()].append(entry.description());
+ return op;
+ }
+
+ static TextureOp textureCopy(QRhiTexture *dst, QRhiTexture *src, const QRhiTextureCopyDescription &desc)
+ {
+ TextureOp op;
+ op.type = Copy;
+ op.copy.dst = dst;
+ op.copy.src = src;
+ op.copy.desc = desc;
+ return op;
+ }
+
+ static TextureOp textureRead(const QRhiReadbackDescription &rb, QRhiReadbackResult *result)
+ {
+ TextureOp op;
+ op.type = Read;
+ op.read.rb = rb;
+ op.read.result = result;
+ return op;
+ }
+
+ static TextureOp textureMipGen(QRhiTexture *tex, int layer)
+ {
+ TextureOp op;
+ op.type = MipGen;
+ op.mipgen.tex = tex;
+ op.mipgen.layer = layer;
+ return op;
+ }
+ };
+
+ QVector<DynamicBufferUpdate> dynamicBufferUpdates;
+ QVector<StaticBufferUpload> staticBufferUploads;
+ QVector<TextureOp> textureOps;
+
+ QRhiResourceUpdateBatch *q = nullptr;
+ QRhiImplementation *rhi = nullptr;
+ int poolIndex = -1;
+
+ void free();
+ void merge(QRhiResourceUpdateBatchPrivate *other);
+
+ static QRhiResourceUpdateBatchPrivate *get(QRhiResourceUpdateBatch *b) { return b->d; }
+};
+
+Q_DECLARE_TYPEINFO(QRhiResourceUpdateBatchPrivate::DynamicBufferUpdate, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(QRhiResourceUpdateBatchPrivate::StaticBufferUpload, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(QRhiResourceUpdateBatchPrivate::TextureOp, Q_MOVABLE_TYPE);
+
+class Q_GUI_EXPORT QRhiShaderResourceBindingPrivate
+{
+public:
+ QRhiShaderResourceBindingPrivate()
+ : ref(1)
+ {
+ }
+
+ QRhiShaderResourceBindingPrivate(const QRhiShaderResourceBindingPrivate *other)
+ : ref(1),
+ binding(other->binding),
+ stage(other->stage),
+ type(other->type),
+ u(other->u)
+ {
+ }
+
+ static QRhiShaderResourceBindingPrivate *get(QRhiShaderResourceBinding *s) { return s->d; }
+ static const QRhiShaderResourceBindingPrivate *get(const QRhiShaderResourceBinding *s) { return s->d; }
+
+ QAtomicInt ref;
+ int binding;
+ QRhiShaderResourceBinding::StageFlags stage;
+ QRhiShaderResourceBinding::Type type;
+ struct UniformBufferData {
+ QRhiBuffer *buf;
+ int offset;
+ int maybeSize;
+ bool hasDynamicOffset;
+ };
+ struct SampledTextureData {
+ QRhiTexture *tex;
+ QRhiSampler *sampler;
+ };
+ union {
+ UniformBufferData ubuf;
+ SampledTextureData stex;
+ } u;
+};
+
+template<typename T>
+struct QRhiBatchedBindings
+{
+ void feed(int binding, T resource) { // binding must be strictly increasing
+ if (curBinding == -1 || binding > curBinding + 1) {
+ finish();
+ curBatch.startBinding = binding;
+ curBatch.resources.clear();
+ curBatch.resources.append(resource);
+ } else {
+ Q_ASSERT(binding == curBinding + 1);
+ curBatch.resources.append(resource);
+ }
+ curBinding = binding;
+ }
+
+ void finish() {
+ if (!curBatch.resources.isEmpty())
+ batches.append(curBatch);
+ }
+
+ void clear() {
+ batches.clear();
+ curBatch.resources.clear();
+ curBinding = -1;
+ }
+
+ struct Batch {
+ uint startBinding;
+ QVarLengthArray<T, 4> resources;
+
+ bool operator==(const Batch &other) const
+ {
+ return startBinding == other.startBinding && resources == other.resources;
+ }
+
+ bool operator!=(const Batch &other) const
+ {
+ return !operator==(other);
+ }
+ };
+
+ QVarLengthArray<Batch, 4> batches; // sorted by startBinding
+
+ bool operator==(const QRhiBatchedBindings<T> &other) const
+ {
+ return batches == other.batches;
+ }
+
+ bool operator!=(const QRhiBatchedBindings<T> &other) const
+ {
+ return !operator==(other);
+ }
+
+private:
+ Batch curBatch;
+ int curBinding = -1;
+};
+
+class QRhiGlobalObjectIdGenerator
+{
+public:
+#ifdef Q_ATOMIC_INT64_IS_SUPPORTED
+ using Type = quint64;
+#else
+ using Type = quint32;
+#endif
+ static Type newId();
+
+private:
+ QAtomicInteger<Type> counter;
+};
+
+class QRhiPassResourceTracker
+{
+public:
+ bool isEmpty() const;
+ void reset();
+
+ struct UsageState {
+ int layout;
+ int access;
+ int stage;
+ };
+
+ enum BufferStage {
+ BufVertexInputStage,
+ BufVertexStage,
+ BufFragmentStage
+ };
+
+ enum BufferAccess {
+ BufVertexInput,
+ BufIndexRead,
+ BufUniformRead
+ };
+
+ void registerBufferOnce(QRhiBuffer *buf, int slot, BufferAccess access, BufferStage stage,
+ const UsageState &stateAtPassBegin);
+
+ enum TextureStage {
+ TexVertexStage,
+ TexFragmentStage,
+ TexColorOutputStage,
+ TexDepthOutputStage
+ };
+
+ enum TextureAccess {
+ TexSample,
+ TexColorOutput,
+ TexDepthOutput
+ };
+
+ void registerTextureOnce(QRhiTexture *tex, TextureAccess access, TextureStage stage,
+ const UsageState &stateAtPassBegin);
+
+ struct Buffer {
+ QRhiBuffer *buf;
+ int slot;
+ BufferAccess access;
+ BufferStage stage;
+ UsageState stateAtPassBegin;
+ };
+ const QVector<Buffer> *buffers() const { return &m_buffers; }
+
+ struct Texture {
+ QRhiTexture *tex;
+ TextureAccess access;
+ TextureStage stage;
+ UsageState stateAtPassBegin;
+ };
+ const QVector<Texture> *textures() const { return &m_textures; }
+
+private:
+ QVector<Buffer> m_buffers;
+ QVector<Texture> m_textures;
+};
+
+Q_DECLARE_TYPEINFO(QRhiPassResourceTracker::Buffer, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(QRhiPassResourceTracker::Texture, Q_MOVABLE_TYPE);
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/rhi/qrhid3d11.cpp b/src/gui/rhi/qrhid3d11.cpp
new file mode 100644
index 0000000000..07e67d95d9
--- /dev/null
+++ b/src/gui/rhi/qrhid3d11.cpp
@@ -0,0 +1,3388 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gui module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qrhid3d11_p_p.h"
+#include "qshader_p.h"
+#include <QWindow>
+#include <QOperatingSystemVersion>
+#include <qmath.h>
+
+#include <d3dcompiler.h>
+#include <comdef.h>
+
+QT_BEGIN_NAMESPACE
+
+/*
+ Direct3D 11 backend. Provides a double-buffered flip model (FLIP_DISCARD)
+ swapchain. Textures and "static" buffers are USAGE_DEFAULT, leaving it to
+ UpdateSubResource to upload the data in any way it sees fit. "Dynamic"
+ buffers are USAGE_DYNAMIC and updating is done by mapping with WRITE_DISCARD.
+ (so here QRhiBuffer keeps a copy of the buffer contents and all of it is
+ memcpy'd every time, leaving the rest (juggling with the memory area Map
+ returns) to the driver).
+*/
+
+/*!
+ \class QRhiD3D11InitParams
+ \inmodule QtRhi
+ \brief Direct3D 11 specific initialization parameters.
+
+ A D3D11-based QRhi needs no special parameters for initialization. If
+ desired, enableDebugLayer can be set to \c true to enable the Direct3D
+ debug layer. This can be useful during development, but should be avoided
+ in production builds.
+
+ \badcode
+ QRhiD3D11InitParams params;
+ params.enableDebugLayer = true;
+ rhi = QRhi::create(QRhi::D3D11, &params);
+ \endcode
+
+ \note QRhiSwapChain should only be used in combination with QWindow
+ instances that have their surface type set to QSurface::OpenGLSurface.
+ There are currently no Direct3D specifics in the Windows platform support
+ of Qt and therefore there is no separate QSurface type available.
+
+ \section2 Working with existing Direct3D 11 devices
+
+ When interoperating with another graphics engine, it may be necessary to
+ get a QRhi instance that uses the same Direct3D device. This can be
+ achieved by passing a pointer to a QRhiD3D11NativeHandles to
+ QRhi::create(). Both the device and the device context must be set to a
+ non-null value then.
+
+ The QRhi does not take ownership of any of the external objects.
+
+ \note QRhi works with immediate contexts only. Deferred contexts are not
+ used in any way.
+
+ \note Regardless of using an imported or a QRhi-created device context, the
+ \c ID3D11DeviceContext1 interface (Direct3D 11.1) must be supported.
+ Initialization will fail otherwise.
+ */
+
+/*!
+ \class QRhiD3D11NativeHandles
+ \inmodule QtRhi
+ \brief Holds the D3D device and device context used by the QRhi.
+
+ \note The class uses \c{void *} as the type since including the COM-based
+ \c{d3d11.h} headers is not acceptable here. The actual types are
+ \c{ID3D11Device *} and \c{ID3D11DeviceContext *}.
+ */
+
+/*!
+ \class QRhiD3D11TextureNativeHandles
+ \inmodule QtRhi
+ \brief Holds the D3D texture object that is backing a QRhiTexture instance.
+
+ \note The class uses \c{void *} as the type since including the COM-based
+ \c{d3d11.h} headers is not acceptable here. The actual type is
+ \c{ID3D11Texture2D *}.
+ */
+
+QRhiD3D11::QRhiD3D11(QRhiD3D11InitParams *params, QRhiD3D11NativeHandles *importDevice)
+ : ofr(this)
+{
+ debugLayer = params->enableDebugLayer;
+ importedDevice = importDevice != nullptr;
+ if (importedDevice) {
+ dev = reinterpret_cast<ID3D11Device *>(importDevice->dev);
+ if (dev) {
+ ID3D11DeviceContext *ctx = reinterpret_cast<ID3D11DeviceContext *>(importDevice->context);
+ if (SUCCEEDED(ctx->QueryInterface(IID_ID3D11DeviceContext1, reinterpret_cast<void **>(&context)))) {
+ // get rid of the ref added by QueryInterface
+ ctx->Release();
+ } else {
+ qWarning("ID3D11DeviceContext1 not supported by context, cannot import");
+ importedDevice = false;
+ }
+ } else {
+ qWarning("No ID3D11Device given, cannot import");
+ importedDevice = false;
+ }
+ }
+}
+
+static QString comErrorMessage(HRESULT hr)
+{
+#ifndef Q_OS_WINRT
+ const _com_error comError(hr);
+#else
+ const _com_error comError(hr, nullptr);
+#endif
+ QString result = QLatin1String("Error 0x") + QString::number(ulong(hr), 16);
+ if (const wchar_t *msg = comError.ErrorMessage())
+ result += QLatin1String(": ") + QString::fromWCharArray(msg);
+ return result;
+}
+
+static inline uint aligned(uint v, uint byteAlign)
+{
+ return (v + byteAlign - 1) & ~(byteAlign - 1);
+}
+
+bool QRhiD3D11::create(QRhi::Flags flags)
+{
+ Q_UNUSED(flags);
+
+ uint devFlags = 0;
+ if (debugLayer)
+ devFlags |= D3D11_CREATE_DEVICE_DEBUG;
+
+ HRESULT hr;
+#if !defined(Q_CC_MINGW)
+ hasDxgi2 = QOperatingSystemVersion::current() > QOperatingSystemVersion::Windows7;
+ if (hasDxgi2)
+ hr = CreateDXGIFactory2(0, IID_IDXGIFactory2, reinterpret_cast<void **>(&dxgiFactory));
+ else
+#endif
+ hr = CreateDXGIFactory1(IID_IDXGIFactory1, reinterpret_cast<void **>(&dxgiFactory));
+
+ if (FAILED(hr)) {
+ qWarning("Failed to create DXGI factory: %s", qPrintable(comErrorMessage(hr)));
+ return false;
+ }
+
+ if (!importedDevice) {
+ IDXGIAdapter1 *adapterToUse = nullptr;
+ IDXGIAdapter1 *adapter;
+ int requestedAdapterIndex = -1;
+ if (qEnvironmentVariableIsSet("QT_D3D_ADAPTER_INDEX"))
+ requestedAdapterIndex = qEnvironmentVariableIntValue("QT_D3D_ADAPTER_INDEX");
+ for (int adapterIndex = 0; dxgiFactory->EnumAdapters1(adapterIndex, &adapter) != DXGI_ERROR_NOT_FOUND; ++adapterIndex) {
+ DXGI_ADAPTER_DESC1 desc;
+ adapter->GetDesc1(&desc);
+ const QString name = QString::fromUtf16((char16_t *) desc.Description);
+ qDebug("Adapter %d: '%s' (flags 0x%x)", adapterIndex, qPrintable(name), desc.Flags);
+ if (!adapterToUse && (requestedAdapterIndex < 0 || requestedAdapterIndex == adapterIndex)) {
+ adapterToUse = adapter;
+ qDebug(" using this adapter");
+ } else {
+ adapter->Release();
+ }
+ }
+ if (!adapterToUse) {
+ qWarning("No adapter");
+ return false;
+ }
+
+ ID3D11DeviceContext *ctx = nullptr;
+ HRESULT hr = D3D11CreateDevice(adapterToUse, D3D_DRIVER_TYPE_UNKNOWN, nullptr, devFlags,
+ nullptr, 0, D3D11_SDK_VERSION,
+ &dev, &featureLevel, &ctx);
+ adapterToUse->Release();
+ if (FAILED(hr)) {
+ qWarning("Failed to create D3D11 device and context: %s", qPrintable(comErrorMessage(hr)));
+ return false;
+ }
+ if (SUCCEEDED(ctx->QueryInterface(IID_ID3D11DeviceContext1, reinterpret_cast<void **>(&context)))) {
+ ctx->Release();
+ } else {
+ qWarning("ID3D11DeviceContext1 not supported");
+ return false;
+ }
+ } else {
+ Q_ASSERT(dev && context);
+ featureLevel = dev->GetFeatureLevel();
+ }
+
+ if (FAILED(context->QueryInterface(IID_ID3DUserDefinedAnnotation, reinterpret_cast<void **>(&annotations))))
+ annotations = nullptr;
+
+ nativeHandlesStruct.dev = dev;
+ nativeHandlesStruct.context = context;
+
+ return true;
+}
+
+void QRhiD3D11::destroy()
+{
+ finishActiveReadbacks();
+
+ if (annotations) {
+ annotations->Release();
+ annotations = nullptr;
+ }
+
+ if (!importedDevice) {
+ if (context) {
+ context->Release();
+ context = nullptr;
+ }
+ if (dev) {
+ dev->Release();
+ dev = nullptr;
+ }
+ }
+
+ if (dxgiFactory) {
+ dxgiFactory->Release();
+ dxgiFactory = nullptr;
+ }
+}
+
+void QRhiD3D11::reportLiveObjects(ID3D11Device *device)
+{
+ // this works only when params.enableDebugLayer was true
+ ID3D11Debug *debug;
+ if (SUCCEEDED(device->QueryInterface(IID_ID3D11Debug, reinterpret_cast<void **>(&debug)))) {
+ debug->ReportLiveDeviceObjects(D3D11_RLDO_DETAIL);
+ debug->Release();
+ }
+}
+
+QVector<int> QRhiD3D11::supportedSampleCounts() const
+{
+ return { 1, 2, 4, 8 };
+}
+
+DXGI_SAMPLE_DESC QRhiD3D11::effectiveSampleCount(int sampleCount) const
+{
+ DXGI_SAMPLE_DESC desc;
+ desc.Count = 1;
+ desc.Quality = 0;
+
+ // Stay compatible with QSurfaceFormat and friends where samples == 0 means the same as 1.
+ int s = qBound(1, sampleCount, 64);
+
+ if (!supportedSampleCounts().contains(s)) {
+ qWarning("Attempted to set unsupported sample count %d", sampleCount);
+ return desc;
+ }
+
+ desc.Count = s;
+ if (s > 1)
+ desc.Quality = D3D11_STANDARD_MULTISAMPLE_PATTERN;
+ else
+ desc.Quality = 0;
+
+ return desc;
+}
+
+QRhiSwapChain *QRhiD3D11::createSwapChain()
+{
+ return new QD3D11SwapChain(this);
+}
+
+QRhiBuffer *QRhiD3D11::createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, int size)
+{
+ return new QD3D11Buffer(this, type, usage, size);
+}
+
+int QRhiD3D11::ubufAlignment() const
+{
+ return 256;
+}
+
+bool QRhiD3D11::isYUpInFramebuffer() const
+{
+ return false;
+}
+
+bool QRhiD3D11::isYUpInNDC() const
+{
+ return true;
+}
+
+bool QRhiD3D11::isClipDepthZeroToOne() const
+{
+ return true;
+}
+
+QMatrix4x4 QRhiD3D11::clipSpaceCorrMatrix() const
+{
+ // Like with Vulkan, but Y is already good.
+
+ static QMatrix4x4 m;
+ if (m.isIdentity()) {
+ // NB the ctor takes row-major
+ m = QMatrix4x4(1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 0.5f, 0.5f,
+ 0.0f, 0.0f, 0.0f, 1.0f);
+ }
+ return m;
+}
+
+bool QRhiD3D11::isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const
+{
+ Q_UNUSED(flags);
+
+ if (format >= QRhiTexture::ETC2_RGB8 && format <= QRhiTexture::ASTC_12x12)
+ return false;
+
+ return true;
+}
+
+bool QRhiD3D11::isFeatureSupported(QRhi::Feature feature) const
+{
+ switch (feature) {
+ case QRhi::MultisampleTexture:
+ return true;
+ case QRhi::MultisampleRenderBuffer:
+ return true;
+ case QRhi::DebugMarkers:
+ return annotations != nullptr;
+ case QRhi::Timestamps:
+ return true;
+ case QRhi::Instancing:
+ return true;
+ case QRhi::CustomInstanceStepRate:
+ return true;
+ case QRhi::PrimitiveRestart:
+ return true;
+ case QRhi::NonDynamicUniformBuffers:
+ return false; // because UpdateSubresource cannot deal with this
+ case QRhi::NonFourAlignedEffectiveIndexBufferOffset:
+ return true;
+ case QRhi::NPOTTextureRepeat:
+ return true;
+ case QRhi::RedOrAlpha8IsRed:
+ return true;
+ case QRhi::ElementIndexUint:
+ return true;
+ default:
+ Q_UNREACHABLE();
+ return false;
+ }
+}
+
+int QRhiD3D11::resourceLimit(QRhi::ResourceLimit limit) const
+{
+ switch (limit) {
+ case QRhi::TextureSizeMin:
+ return 1;
+ case QRhi::TextureSizeMax:
+ return D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION;
+ case QRhi::MaxColorAttachments:
+ return 8;
+ case QRhi::FramesInFlight:
+ return 2; // dummy
+ default:
+ Q_UNREACHABLE();
+ return 0;
+ }
+}
+
+const QRhiNativeHandles *QRhiD3D11::nativeHandles()
+{
+ return &nativeHandlesStruct;
+}
+
+void QRhiD3D11::sendVMemStatsToProfiler()
+{
+ // nothing to do here
+}
+
+QRhiRenderBuffer *QRhiD3D11::createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize,
+ int sampleCount, QRhiRenderBuffer::Flags flags)
+{
+ return new QD3D11RenderBuffer(this, type, pixelSize, sampleCount, flags);
+}
+
+QRhiTexture *QRhiD3D11::createTexture(QRhiTexture::Format format, const QSize &pixelSize,
+ int sampleCount, QRhiTexture::Flags flags)
+{
+ return new QD3D11Texture(this, format, pixelSize, sampleCount, flags);
+}
+
+QRhiSampler *QRhiD3D11::createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
+ QRhiSampler::Filter mipmapMode,
+ QRhiSampler::AddressMode u, QRhiSampler::AddressMode v)
+{
+ return new QD3D11Sampler(this, magFilter, minFilter, mipmapMode, u, v);
+}
+
+QRhiTextureRenderTarget *QRhiD3D11::createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
+ QRhiTextureRenderTarget::Flags flags)
+{
+ return new QD3D11TextureRenderTarget(this, desc, flags);
+}
+
+QRhiGraphicsPipeline *QRhiD3D11::createGraphicsPipeline()
+{
+ return new QD3D11GraphicsPipeline(this);
+}
+
+QRhiShaderResourceBindings *QRhiD3D11::createShaderResourceBindings()
+{
+ return new QD3D11ShaderResourceBindings(this);
+}
+
+void QRhiD3D11::setGraphicsPipeline(QRhiCommandBuffer *cb, QRhiGraphicsPipeline *ps)
+{
+ Q_ASSERT(inPass);
+
+ QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
+ QD3D11GraphicsPipeline *psD = QRHI_RES(QD3D11GraphicsPipeline, ps);
+ const bool pipelineChanged = cbD->currentPipeline != ps || cbD->currentPipelineGeneration != psD->generation;
+
+ if (pipelineChanged) {
+ cbD->currentPipeline = ps;
+ cbD->currentPipelineGeneration = psD->generation;
+
+ QD3D11CommandBuffer::Command cmd;
+ cmd.cmd = QD3D11CommandBuffer::Command::BindGraphicsPipeline;
+ cmd.args.bindGraphicsPipeline.ps = psD;
+ cbD->commands.append(cmd);
+ }
+}
+
+void QRhiD3D11::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBindings *srb,
+ int dynamicOffsetCount,
+ const QRhiCommandBuffer::DynamicOffset *dynamicOffsets)
+{
+ Q_ASSERT(inPass);
+
+ QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
+ Q_ASSERT(cbD->currentPipeline);
+
+ if (!srb)
+ srb = QRHI_RES(QD3D11GraphicsPipeline, cbD->currentPipeline)->m_shaderResourceBindings;
+
+ QD3D11ShaderResourceBindings *srbD = QRHI_RES(QD3D11ShaderResourceBindings, srb);
+
+ bool hasDynamicOffsetInSrb = false;
+ bool srbUpdate = false;
+ for (int i = 0, ie = srbD->sortedBindings.count(); i != ie; ++i) {
+ const QRhiShaderResourceBindingPrivate *b = QRhiShaderResourceBindingPrivate::get(&srbD->sortedBindings[i]);
+ QD3D11ShaderResourceBindings::BoundResourceData &bd(srbD->boundResourceData[i]);
+ switch (b->type) {
+ case QRhiShaderResourceBinding::UniformBuffer:
+ {
+ QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, b->u.ubuf.buf);
+ if (bufD->m_type == QRhiBuffer::Dynamic)
+ executeBufferHostWritesForCurrentFrame(bufD);
+
+ if (bufD->generation != bd.ubuf.generation || bufD->m_id != bd.ubuf.id) {
+ srbUpdate = true;
+ bd.ubuf.id = bufD->m_id;
+ bd.ubuf.generation = bufD->generation;
+ }
+
+ if (b->u.ubuf.hasDynamicOffset)
+ hasDynamicOffsetInSrb = true;
+ }
+ break;
+ case QRhiShaderResourceBinding::SampledTexture:
+ {
+ QD3D11Texture *texD = QRHI_RES(QD3D11Texture, b->u.stex.tex);
+ QD3D11Sampler *samplerD = QRHI_RES(QD3D11Sampler, b->u.stex.sampler);
+ if (texD->generation != bd.stex.texGeneration
+ || texD->m_id != bd.stex.texId
+ || samplerD->generation != bd.stex.samplerGeneration
+ || samplerD->m_id != bd.stex.samplerId)
+ {
+ srbUpdate = true;
+ bd.stex.texId = texD->m_id;
+ bd.stex.texGeneration = texD->generation;
+ bd.stex.samplerId = samplerD->m_id;
+ bd.stex.samplerGeneration = samplerD->generation;
+ }
+ }
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ }
+
+ if (srbUpdate)
+ updateShaderResourceBindings(srbD);
+
+ const bool srbChanged = cbD->currentSrb != srb || cbD->currentSrbGeneration != srbD->generation;
+
+ if (srbChanged || srbUpdate || hasDynamicOffsetInSrb) {
+ cbD->currentSrb = srb;
+ cbD->currentSrbGeneration = srbD->generation;
+
+ QD3D11CommandBuffer::Command cmd;
+ cmd.cmd = QD3D11CommandBuffer::Command::BindShaderResources;
+ cmd.args.bindShaderResources.srb = srbD;
+ // dynamic offsets have to be applied at the time of executing the bind
+ // operations, not here
+ cmd.args.bindShaderResources.offsetOnlyChange = !srbChanged && !srbUpdate && hasDynamicOffsetInSrb;
+ cmd.args.bindShaderResources.dynamicOffsetCount = 0;
+ if (hasDynamicOffsetInSrb) {
+ if (dynamicOffsetCount < QD3D11CommandBuffer::Command::MAX_UBUF_BINDINGS) {
+ cmd.args.bindShaderResources.dynamicOffsetCount = dynamicOffsetCount;
+ uint *p = cmd.args.bindShaderResources.dynamicOffsetPairs;
+ for (int i = 0; i < dynamicOffsetCount; ++i) {
+ const QRhiCommandBuffer::DynamicOffset &dynOfs(dynamicOffsets[i]);
+ const uint binding = dynOfs.first;
+ Q_ASSERT(aligned(dynOfs.second, 256) == dynOfs.second);
+ const uint offsetInConstants = dynOfs.second / 16;
+ *p++ = binding;
+ *p++ = offsetInConstants;
+ }
+ } else {
+ qWarning("Too many dynamic offsets (%d, max is %d)",
+ dynamicOffsetCount, QD3D11CommandBuffer::Command::MAX_UBUF_BINDINGS);
+ }
+ }
+
+ cbD->commands.append(cmd);
+ }
+}
+
+void QRhiD3D11::setVertexInput(QRhiCommandBuffer *cb,
+ int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings,
+ QRhiBuffer *indexBuf, quint32 indexOffset, QRhiCommandBuffer::IndexFormat indexFormat)
+{
+ Q_ASSERT(inPass);
+ QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
+
+ bool needsBindVBuf = false;
+ for (int i = 0; i < bindingCount; ++i) {
+ const int inputSlot = startBinding + i;
+ QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, bindings[i].first);
+ Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::VertexBuffer));
+ if (bufD->m_type == QRhiBuffer::Dynamic)
+ executeBufferHostWritesForCurrentFrame(bufD);
+
+ if (cbD->currentVertexBuffers[inputSlot] != bufD->buffer
+ || cbD->currentVertexOffsets[inputSlot] != bindings[i].second)
+ {
+ needsBindVBuf = true;
+ cbD->currentVertexBuffers[inputSlot] = bufD->buffer;
+ cbD->currentVertexOffsets[inputSlot] = bindings[i].second;
+ }
+ }
+
+ if (needsBindVBuf) {
+ QD3D11CommandBuffer::Command cmd;
+ cmd.cmd = QD3D11CommandBuffer::Command::BindVertexBuffers;
+ cmd.args.bindVertexBuffers.startSlot = startBinding;
+ cmd.args.bindVertexBuffers.slotCount = bindingCount;
+ const QVector<QRhiVertexInputBinding> inputBindings =
+ QRHI_RES(QD3D11GraphicsPipeline, cbD->currentPipeline)->m_vertexInputLayout.bindings();
+ for (int i = 0, ie = qMin(bindingCount, inputBindings.count()); i != ie; ++i) {
+ QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, bindings[i].first);
+ cmd.args.bindVertexBuffers.buffers[i] = bufD->buffer;
+ cmd.args.bindVertexBuffers.offsets[i] = bindings[i].second;
+ cmd.args.bindVertexBuffers.strides[i] = inputBindings[i].stride();
+ }
+ cbD->commands.append(cmd);
+ }
+
+ if (indexBuf) {
+ QD3D11Buffer *ibufD = QRHI_RES(QD3D11Buffer, indexBuf);
+ Q_ASSERT(ibufD->m_usage.testFlag(QRhiBuffer::IndexBuffer));
+ if (ibufD->m_type == QRhiBuffer::Dynamic)
+ executeBufferHostWritesForCurrentFrame(ibufD);
+
+ const DXGI_FORMAT dxgiFormat = indexFormat == QRhiCommandBuffer::IndexUInt16 ? DXGI_FORMAT_R16_UINT
+ : DXGI_FORMAT_R32_UINT;
+ if (cbD->currentIndexBuffer != ibufD->buffer
+ || cbD->currentIndexOffset != indexOffset
+ || cbD->currentIndexFormat != dxgiFormat)
+ {
+ cbD->currentIndexBuffer = ibufD->buffer;
+ cbD->currentIndexOffset = indexOffset;
+ cbD->currentIndexFormat = dxgiFormat;
+
+ QD3D11CommandBuffer::Command cmd;
+ cmd.cmd = QD3D11CommandBuffer::Command::BindIndexBuffer;
+ cmd.args.bindIndexBuffer.buffer = ibufD->buffer;
+ cmd.args.bindIndexBuffer.offset = indexOffset;
+ cmd.args.bindIndexBuffer.format = dxgiFormat;
+ cbD->commands.append(cmd);
+ }
+ }
+}
+
+void QRhiD3D11::setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport)
+{
+ Q_ASSERT(inPass);
+ QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
+ Q_ASSERT(cbD->currentTarget);
+ const QSize outputSize = cbD->currentTarget->pixelSize();
+
+ QD3D11CommandBuffer::Command cmd;
+ cmd.cmd = QD3D11CommandBuffer::Command::Viewport;
+
+ // d3d expects top-left, QRhiViewport is bottom-left
+ float x, y, w, h;
+ if (!qrhi_toTopLeftRenderTargetRect(outputSize, viewport.viewport(), &x, &y, &w, &h))
+ return;
+
+ cmd.args.viewport.x = x;
+ cmd.args.viewport.y = y;
+ cmd.args.viewport.w = w;
+ cmd.args.viewport.h = h;
+ cmd.args.viewport.d0 = viewport.minDepth();
+ cmd.args.viewport.d1 = viewport.maxDepth();
+ cbD->commands.append(cmd);
+}
+
+void QRhiD3D11::setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor)
+{
+ Q_ASSERT(inPass);
+ QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
+ Q_ASSERT(cbD->currentTarget);
+ const QSize outputSize = cbD->currentTarget->pixelSize();
+
+ QD3D11CommandBuffer::Command cmd;
+ cmd.cmd = QD3D11CommandBuffer::Command::Scissor;
+
+ // d3d expects top-left, QRhiScissor is bottom-left
+ int x, y, w, h;
+ if (!qrhi_toTopLeftRenderTargetRect(outputSize, scissor.scissor(), &x, &y, &w, &h))
+ return;
+
+ cmd.args.scissor.x = x;
+ cmd.args.scissor.y = y;
+ cmd.args.scissor.w = w;
+ cmd.args.scissor.h = h;
+ cbD->commands.append(cmd);
+}
+
+void QRhiD3D11::setBlendConstants(QRhiCommandBuffer *cb, const QColor &c)
+{
+ Q_ASSERT(inPass);
+ QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
+ QD3D11CommandBuffer::Command cmd;
+ cmd.cmd = QD3D11CommandBuffer::Command::BlendConstants;
+ cmd.args.blendConstants.ps = QRHI_RES(QD3D11GraphicsPipeline, cbD->currentPipeline);
+ cmd.args.blendConstants.c[0] = c.redF();
+ cmd.args.blendConstants.c[1] = c.greenF();
+ cmd.args.blendConstants.c[2] = c.blueF();
+ cmd.args.blendConstants.c[3] = c.alphaF();
+ cbD->commands.append(cmd);
+}
+
+void QRhiD3D11::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue)
+{
+ Q_ASSERT(inPass);
+ QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
+ QD3D11CommandBuffer::Command cmd;
+ cmd.cmd = QD3D11CommandBuffer::Command::StencilRef;
+ cmd.args.stencilRef.ps = QRHI_RES(QD3D11GraphicsPipeline, cbD->currentPipeline);
+ cmd.args.stencilRef.ref = refValue;
+ cbD->commands.append(cmd);
+}
+
+void QRhiD3D11::draw(QRhiCommandBuffer *cb, quint32 vertexCount,
+ quint32 instanceCount, quint32 firstVertex, quint32 firstInstance)
+{
+ Q_ASSERT(inPass);
+ QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
+ QD3D11CommandBuffer::Command cmd;
+ cmd.cmd = QD3D11CommandBuffer::Command::Draw;
+ cmd.args.draw.ps = QRHI_RES(QD3D11GraphicsPipeline, cbD->currentPipeline);
+ cmd.args.draw.vertexCount = vertexCount;
+ cmd.args.draw.instanceCount = instanceCount;
+ cmd.args.draw.firstVertex = firstVertex;
+ cmd.args.draw.firstInstance = firstInstance;
+ cbD->commands.append(cmd);
+}
+
+void QRhiD3D11::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
+ quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance)
+{
+ Q_ASSERT(inPass);
+ QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
+ QD3D11CommandBuffer::Command cmd;
+ cmd.cmd = QD3D11CommandBuffer::Command::DrawIndexed;
+ cmd.args.drawIndexed.ps = QRHI_RES(QD3D11GraphicsPipeline, cbD->currentPipeline);
+ cmd.args.drawIndexed.indexCount = indexCount;
+ cmd.args.drawIndexed.instanceCount = instanceCount;
+ cmd.args.drawIndexed.firstIndex = firstIndex;
+ cmd.args.drawIndexed.vertexOffset = vertexOffset;
+ cmd.args.drawIndexed.firstInstance = firstInstance;
+ cbD->commands.append(cmd);
+}
+
+void QRhiD3D11::debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name)
+{
+ if (!debugMarkers || !annotations)
+ return;
+
+ QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
+ QD3D11CommandBuffer::Command cmd;
+ cmd.cmd = QD3D11CommandBuffer::Command::DebugMarkBegin;
+ strncpy(cmd.args.debugMark.s, name.constData(), sizeof(cmd.args.debugMark.s));
+ cmd.args.debugMark.s[sizeof(cmd.args.debugMark.s) - 1] = '\0';
+ cbD->commands.append(cmd);
+}
+
+void QRhiD3D11::debugMarkEnd(QRhiCommandBuffer *cb)
+{
+ if (!debugMarkers || !annotations)
+ return;
+
+ QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
+ QD3D11CommandBuffer::Command cmd;
+ cmd.cmd = QD3D11CommandBuffer::Command::DebugMarkEnd;
+ cbD->commands.append(cmd);
+}
+
+void QRhiD3D11::debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg)
+{
+ if (!debugMarkers || !annotations)
+ return;
+
+ QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
+ QD3D11CommandBuffer::Command cmd;
+ cmd.cmd = QD3D11CommandBuffer::Command::DebugMarkMsg;
+ strncpy(cmd.args.debugMark.s, msg.constData(), sizeof(cmd.args.debugMark.s));
+ cmd.args.debugMark.s[sizeof(cmd.args.debugMark.s) - 1] = '\0';
+ cbD->commands.append(cmd);
+}
+
+const QRhiNativeHandles *QRhiD3D11::nativeHandles(QRhiCommandBuffer *cb)
+{
+ Q_UNUSED(cb);
+ return nullptr;
+}
+
+void QRhiD3D11::beginExternal(QRhiCommandBuffer *cb)
+{
+ Q_ASSERT(inPass);
+ Q_UNUSED(cb);
+ flushCommandBuffer();
+}
+
+void QRhiD3D11::endExternal(QRhiCommandBuffer *cb)
+{
+ Q_ASSERT(inPass);
+ QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
+ Q_ASSERT(cbD->currentTarget);
+ Q_ASSERT(cbD->commands.isEmpty());
+ cbD->resetCachedState();
+ enqueueSetRenderTarget(cbD, cbD->currentTarget);
+}
+
+QRhi::FrameOpResult QRhiD3D11::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags)
+{
+ Q_UNUSED(flags);
+
+ Q_ASSERT(!inFrame);
+ inFrame = true;
+
+ QD3D11SwapChain *swapChainD = QRHI_RES(QD3D11SwapChain, swapChain);
+ contextState.currentSwapChain = swapChainD;
+ const int currentFrameSlot = swapChainD->currentFrameSlot;
+ QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
+
+ if (swapChainD->timestampActive[currentFrameSlot]) {
+ ID3D11Query *tsDisjoint = swapChainD->timestampDisjointQuery[currentFrameSlot];
+ const int tsIdx = QD3D11SwapChain::BUFFER_COUNT * currentFrameSlot;
+ ID3D11Query *tsStart = swapChainD->timestampQuery[tsIdx];
+ ID3D11Query *tsEnd = swapChainD->timestampQuery[tsIdx + 1];
+ quint64 timestamps[2];
+ D3D11_QUERY_DATA_TIMESTAMP_DISJOINT dj;
+ bool ok = true;
+ ok &= context->GetData(tsDisjoint, &dj, sizeof(dj), D3D11_ASYNC_GETDATA_DONOTFLUSH) == S_OK;
+ ok &= context->GetData(tsEnd, &timestamps[1], sizeof(quint64), D3D11_ASYNC_GETDATA_DONOTFLUSH) == S_OK;
+ // this above is often not ready, not even in frame_where_recorded+2,
+ // not clear why. so make the whole thing async and do not touch the
+ // queries until they are finally all available in frame this+2 or
+ // this+4 or ...
+ ok &= context->GetData(tsStart, &timestamps[0], sizeof(quint64), D3D11_ASYNC_GETDATA_DONOTFLUSH) == S_OK;
+ if (ok) {
+ if (!dj.Disjoint && dj.Frequency) {
+ const float elapsedMs = (timestamps[1] - timestamps[0]) / float(dj.Frequency) * 1000.0f;
+ // finally got a value, just report it, the profiler cares about min/max/avg anyway
+ QRHI_PROF_F(swapChainFrameGpuTime(swapChain, elapsedMs));
+ }
+ swapChainD->timestampActive[currentFrameSlot] = false;
+ } // else leave timestampActive set to true, will retry in a subsequent beginFrame
+ }
+
+ swapChainD->cb.resetState();
+
+ swapChainD->rt.d.rtv[0] = swapChainD->sampleDesc.Count > 1 ?
+ swapChainD->msaaRtv[currentFrameSlot] : swapChainD->rtv[currentFrameSlot];
+ swapChainD->rt.d.dsv = swapChainD->ds ? swapChainD->ds->dsv : nullptr;
+
+ QRHI_PROF_F(beginSwapChainFrame(swapChain));
+
+ finishActiveReadbacks();
+
+ return QRhi::FrameOpSuccess;
+}
+
+QRhi::FrameOpResult QRhiD3D11::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags)
+{
+ Q_ASSERT(inFrame);
+ inFrame = false;
+
+ QD3D11SwapChain *swapChainD = QRHI_RES(QD3D11SwapChain, swapChain);
+ Q_ASSERT(contextState.currentSwapChain = swapChainD);
+ const int currentFrameSlot = swapChainD->currentFrameSlot;
+
+ ID3D11Query *tsDisjoint = swapChainD->timestampDisjointQuery[currentFrameSlot];
+ const int tsIdx = QD3D11SwapChain::BUFFER_COUNT * currentFrameSlot;
+ ID3D11Query *tsStart = swapChainD->timestampQuery[tsIdx];
+ ID3D11Query *tsEnd = swapChainD->timestampQuery[tsIdx + 1];
+ const bool recordTimestamps = tsDisjoint && tsStart && tsEnd && !swapChainD->timestampActive[currentFrameSlot];
+
+ // send all commands to the context
+ if (recordTimestamps)
+ executeCommandBuffer(&swapChainD->cb, swapChainD);
+ else
+ executeCommandBuffer(&swapChainD->cb);
+
+ if (swapChainD->sampleDesc.Count > 1) {
+ context->ResolveSubresource(swapChainD->tex[currentFrameSlot], 0,
+ swapChainD->msaaTex[currentFrameSlot], 0,
+ swapChainD->colorFormat);
+ }
+
+ // this is here because we want to include the time spent on the resolve as well
+ if (recordTimestamps) {
+ context->End(tsEnd);
+ context->End(tsDisjoint);
+ swapChainD->timestampActive[currentFrameSlot] = true;
+ }
+
+ QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
+ // this must be done before the Present
+ QRHI_PROF_F(endSwapChainFrame(swapChain, swapChainD->frameCount + 1));
+
+ if (!flags.testFlag(QRhi::SkipPresent)) {
+ const UINT presentFlags = 0;
+ HRESULT hr = swapChainD->swapChain->Present(swapChainD->swapInterval, presentFlags);
+ if (FAILED(hr))
+ qWarning("Failed to present: %s", qPrintable(comErrorMessage(hr)));
+
+ // move on to the next buffer
+ swapChainD->currentFrameSlot = (swapChainD->currentFrameSlot + 1) % QD3D11SwapChain::BUFFER_COUNT;
+ } else {
+ context->Flush();
+ }
+
+ swapChainD->frameCount += 1;
+ contextState.currentSwapChain = nullptr;
+ return QRhi::FrameOpSuccess;
+}
+
+QRhi::FrameOpResult QRhiD3D11::beginOffscreenFrame(QRhiCommandBuffer **cb)
+{
+ Q_ASSERT(!inFrame);
+ inFrame = true;
+ ofr.active = true;
+
+ ofr.cbWrapper.resetState();
+ *cb = &ofr.cbWrapper;
+
+ return QRhi::FrameOpSuccess;
+}
+
+QRhi::FrameOpResult QRhiD3D11::endOffscreenFrame()
+{
+ Q_ASSERT(inFrame && ofr.active);
+ inFrame = false;
+ ofr.active = false;
+
+ executeCommandBuffer(&ofr.cbWrapper);
+
+ finishActiveReadbacks();
+
+ return QRhi::FrameOpSuccess;
+}
+
+static inline DXGI_FORMAT toD3DTextureFormat(QRhiTexture::Format format, QRhiTexture::Flags flags)
+{
+ const bool srgb = flags.testFlag(QRhiTexture::sRGB);
+ switch (format) {
+ case QRhiTexture::RGBA8:
+ return srgb ? DXGI_FORMAT_R8G8B8A8_UNORM_SRGB : DXGI_FORMAT_R8G8B8A8_UNORM;
+ case QRhiTexture::BGRA8:
+ return srgb ? DXGI_FORMAT_B8G8R8A8_UNORM_SRGB : DXGI_FORMAT_B8G8R8A8_UNORM;
+ case QRhiTexture::R8:
+ return DXGI_FORMAT_R8_UNORM;
+ case QRhiTexture::R16:
+ return DXGI_FORMAT_R16_UNORM;
+ case QRhiTexture::RED_OR_ALPHA8:
+ return DXGI_FORMAT_R8_UNORM;
+
+ case QRhiTexture::RGBA16F:
+ return DXGI_FORMAT_R16G16B16A16_FLOAT;
+ case QRhiTexture::RGBA32F:
+ return DXGI_FORMAT_R32G32B32A32_FLOAT;
+
+ case QRhiTexture::D16:
+ return DXGI_FORMAT_R16_TYPELESS;
+ case QRhiTexture::D32F:
+ return DXGI_FORMAT_R32_TYPELESS;
+
+ case QRhiTexture::BC1:
+ return srgb ? DXGI_FORMAT_BC1_UNORM_SRGB : DXGI_FORMAT_BC1_UNORM;
+ case QRhiTexture::BC2:
+ return srgb ? DXGI_FORMAT_BC2_UNORM_SRGB : DXGI_FORMAT_BC2_UNORM;
+ case QRhiTexture::BC3:
+ return srgb ? DXGI_FORMAT_BC3_UNORM_SRGB : DXGI_FORMAT_BC3_UNORM;
+ case QRhiTexture::BC4:
+ return DXGI_FORMAT_BC4_UNORM;
+ case QRhiTexture::BC5:
+ return DXGI_FORMAT_BC5_UNORM;
+ case QRhiTexture::BC6H:
+ return DXGI_FORMAT_BC6H_UF16;
+ case QRhiTexture::BC7:
+ return srgb ? DXGI_FORMAT_BC7_UNORM_SRGB : DXGI_FORMAT_BC7_UNORM;
+
+ case QRhiTexture::ETC2_RGB8:
+ Q_FALLTHROUGH();
+ case QRhiTexture::ETC2_RGB8A1:
+ Q_FALLTHROUGH();
+ case QRhiTexture::ETC2_RGBA8:
+ qWarning("QRhiD3D11 does not support ETC2 textures");
+ return DXGI_FORMAT_R8G8B8A8_UNORM;
+
+ case QRhiTexture::ASTC_4x4:
+ Q_FALLTHROUGH();
+ case QRhiTexture::ASTC_5x4:
+ Q_FALLTHROUGH();
+ case QRhiTexture::ASTC_5x5:
+ Q_FALLTHROUGH();
+ case QRhiTexture::ASTC_6x5:
+ Q_FALLTHROUGH();
+ case QRhiTexture::ASTC_6x6:
+ Q_FALLTHROUGH();
+ case QRhiTexture::ASTC_8x5:
+ Q_FALLTHROUGH();
+ case QRhiTexture::ASTC_8x6:
+ Q_FALLTHROUGH();
+ case QRhiTexture::ASTC_8x8:
+ Q_FALLTHROUGH();
+ case QRhiTexture::ASTC_10x5:
+ Q_FALLTHROUGH();
+ case QRhiTexture::ASTC_10x6:
+ Q_FALLTHROUGH();
+ case QRhiTexture::ASTC_10x8:
+ Q_FALLTHROUGH();
+ case QRhiTexture::ASTC_10x10:
+ Q_FALLTHROUGH();
+ case QRhiTexture::ASTC_12x10:
+ Q_FALLTHROUGH();
+ case QRhiTexture::ASTC_12x12:
+ qWarning("QRhiD3D11 does not support ASTC textures");
+ return DXGI_FORMAT_R8G8B8A8_UNORM;
+
+ default:
+ Q_UNREACHABLE();
+ return DXGI_FORMAT_R8G8B8A8_UNORM;
+ }
+}
+
+static inline QRhiTexture::Format colorTextureFormatFromDxgiFormat(DXGI_FORMAT format, QRhiTexture::Flags *flags)
+{
+ switch (format) {
+ case DXGI_FORMAT_R8G8B8A8_UNORM:
+ return QRhiTexture::RGBA8;
+ case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
+ if (flags)
+ (*flags) |= QRhiTexture::sRGB;
+ return QRhiTexture::RGBA8;
+ case DXGI_FORMAT_B8G8R8A8_UNORM:
+ return QRhiTexture::BGRA8;
+ case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB:
+ if (flags)
+ (*flags) |= QRhiTexture::sRGB;
+ return QRhiTexture::BGRA8;
+ case DXGI_FORMAT_R8_UNORM:
+ return QRhiTexture::R8;
+ case DXGI_FORMAT_R16_UNORM:
+ return QRhiTexture::R16;
+ default: // this cannot assert, must warn and return unknown
+ qWarning("DXGI_FORMAT %d is not a recognized uncompressed color format", format);
+ break;
+ }
+ return QRhiTexture::UnknownFormat;
+}
+
+static inline bool isDepthTextureFormat(QRhiTexture::Format format)
+{
+ switch (format) {
+ case QRhiTexture::Format::D16:
+ Q_FALLTHROUGH();
+ case QRhiTexture::Format::D32F:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+QRhi::FrameOpResult QRhiD3D11::finish()
+{
+ Q_ASSERT(!inPass);
+
+ if (inFrame)
+ flushCommandBuffer();
+
+ finishActiveReadbacks();
+
+ return QRhi::FrameOpSuccess;
+}
+
+void QRhiD3D11::flushCommandBuffer()
+{
+ if (ofr.active) {
+ Q_ASSERT(!contextState.currentSwapChain);
+ executeCommandBuffer(&ofr.cbWrapper);
+ ofr.cbWrapper.resetCommands();
+ } else {
+ Q_ASSERT(contextState.currentSwapChain);
+ executeCommandBuffer(&contextState.currentSwapChain->cb); // no timestampSwapChain, in order to avoid timestamp mess
+ contextState.currentSwapChain->cb.resetCommands();
+ }
+}
+
+void QRhiD3D11::enqueueSubresUpload(QD3D11Texture *texD, QD3D11CommandBuffer *cbD,
+ int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc)
+{
+ UINT subres = D3D11CalcSubresource(level, layer, texD->mipLevelCount);
+ const QPoint dp = subresDesc.destinationTopLeft();
+ D3D11_BOX box;
+ box.front = 0;
+ // back, right, bottom are exclusive
+ box.back = 1;
+ QD3D11CommandBuffer::Command cmd;
+ cmd.cmd = QD3D11CommandBuffer::Command::UpdateSubRes;
+ cmd.args.updateSubRes.dst = texD->tex;
+ cmd.args.updateSubRes.dstSubRes = subres;
+
+ bool cmdValid = true;
+ if (!subresDesc.image().isNull()) {
+ QImage img = subresDesc.image();
+ QSize size = img.size();
+ int bpl = img.bytesPerLine();
+ if (!subresDesc.sourceSize().isEmpty() || !subresDesc.sourceTopLeft().isNull()) {
+ const QPoint sp = subresDesc.sourceTopLeft();
+ if (!subresDesc.sourceSize().isEmpty())
+ size = subresDesc.sourceSize();
+ if (img.depth() == 32) {
+ const int offset = sp.y() * img.bytesPerLine() + sp.x() * 4;
+ cmd.args.updateSubRes.src = cbD->retainImage(img) + offset;
+ } else {
+ img = img.copy(sp.x(), sp.y(), size.width(), size.height());
+ bpl = img.bytesPerLine();
+ cmd.args.updateSubRes.src = cbD->retainImage(img);
+ }
+ } else {
+ cmd.args.updateSubRes.src = cbD->retainImage(img);
+ }
+ box.left = dp.x();
+ box.top = dp.y();
+ box.right = dp.x() + size.width();
+ box.bottom = dp.y() + size.height();
+ cmd.args.updateSubRes.hasDstBox = true;
+ cmd.args.updateSubRes.dstBox = box;
+ cmd.args.updateSubRes.srcRowPitch = bpl;
+ } else if (!subresDesc.data().isEmpty() && isCompressedFormat(texD->m_format)) {
+ const QSize size = subresDesc.sourceSize().isEmpty() ? q->sizeForMipLevel(level, texD->m_pixelSize)
+ : subresDesc.sourceSize();
+ quint32 bpl = 0;
+ QSize blockDim;
+ compressedFormatInfo(texD->m_format, size, &bpl, nullptr, &blockDim);
+ // Everything must be a multiple of the block width and
+ // height, so e.g. a mip level of size 2x2 will be 4x4 when it
+ // comes to the actual data.
+ box.left = aligned(dp.x(), blockDim.width());
+ box.top = aligned(dp.y(), blockDim.height());
+ box.right = aligned(dp.x() + size.width(), blockDim.width());
+ box.bottom = aligned(dp.y() + size.height(), blockDim.height());
+ cmd.args.updateSubRes.hasDstBox = true;
+ cmd.args.updateSubRes.dstBox = box;
+ cmd.args.updateSubRes.src = cbD->retainData(subresDesc.data());
+ cmd.args.updateSubRes.srcRowPitch = bpl;
+ } else if (!subresDesc.data().isEmpty()) {
+ const QSize size = subresDesc.sourceSize().isEmpty() ? q->sizeForMipLevel(level, texD->m_pixelSize)
+ : subresDesc.sourceSize();
+ quint32 bpl = 0;
+ QSize blockDim;
+ textureFormatInfo(texD->m_format, size, &bpl, nullptr);
+ box.left = dp.x();
+ box.top = dp.y();
+ box.right = dp.x() + size.width();
+ box.bottom = dp.y() + size.height();
+ cmd.args.updateSubRes.hasDstBox = true;
+ cmd.args.updateSubRes.dstBox = box;
+ cmd.args.updateSubRes.src = cbD->retainData(subresDesc.data());
+ cmd.args.updateSubRes.srcRowPitch = bpl;
+ } else {
+ qWarning("Invalid texture upload for %p layer=%d mip=%d", texD, layer, level);
+ cmdValid = false;
+ }
+ if (cmdValid)
+ cbD->commands.append(cmd);
+}
+
+void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
+ QRhiResourceUpdateBatchPrivate *ud = QRhiResourceUpdateBatchPrivate::get(resourceUpdates);
+ QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
+
+ for (const QRhiResourceUpdateBatchPrivate::DynamicBufferUpdate &u : ud->dynamicBufferUpdates) {
+ QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, u.buf);
+ Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic);
+ memcpy(bufD->dynBuf.data() + u.offset, u.data.constData(), u.data.size());
+ bufD->hasPendingDynamicUpdates = true;
+ }
+
+ for (const QRhiResourceUpdateBatchPrivate::StaticBufferUpload &u : ud->staticBufferUploads) {
+ QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, u.buf);
+ Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic);
+ Q_ASSERT(u.offset + u.data.size() <= bufD->m_size);
+ QD3D11CommandBuffer::Command cmd;
+ cmd.cmd = QD3D11CommandBuffer::Command::UpdateSubRes;
+ cmd.args.updateSubRes.dst = bufD->buffer;
+ cmd.args.updateSubRes.dstSubRes = 0;
+ cmd.args.updateSubRes.src = cbD->retainData(u.data);
+ cmd.args.updateSubRes.srcRowPitch = 0;
+ // Specify the region (even when offset is 0 and all data is provided)
+ // since the ID3D11Buffer's size is rounded up to be a multiple of 256
+ // while the data we have has the original size.
+ D3D11_BOX box;
+ box.left = u.offset;
+ box.top = box.front = 0;
+ box.back = box.bottom = 1;
+ box.right = u.offset + u.data.size(); // no -1: right, bottom, back are exclusive, see D3D11_BOX doc
+ cmd.args.updateSubRes.hasDstBox = true;
+ cmd.args.updateSubRes.dstBox = box;
+ cbD->commands.append(cmd);
+ }
+
+ for (const QRhiResourceUpdateBatchPrivate::TextureOp &u : ud->textureOps) {
+ if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Upload) {
+ QD3D11Texture *texD = QRHI_RES(QD3D11Texture, u.upload.tex);
+ for (int layer = 0; layer < QRhi::MAX_LAYERS; ++layer) {
+ for (int level = 0; level < QRhi::MAX_LEVELS; ++level) {
+ for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.upload.subresDesc[layer][level]))
+ enqueueSubresUpload(texD, cbD, layer, level, subresDesc);
+ }
+ }
+ } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Copy) {
+ Q_ASSERT(u.copy.src && u.copy.dst);
+ QD3D11Texture *srcD = QRHI_RES(QD3D11Texture, u.copy.src);
+ QD3D11Texture *dstD = QRHI_RES(QD3D11Texture, u.copy.dst);
+ UINT srcSubRes = D3D11CalcSubresource(u.copy.desc.sourceLevel(), u.copy.desc.sourceLayer(), srcD->mipLevelCount);
+ UINT dstSubRes = D3D11CalcSubresource(u.copy.desc.destinationLevel(), u.copy.desc.destinationLayer(), dstD->mipLevelCount);
+ const QPoint dp = u.copy.desc.destinationTopLeft();
+ const QSize size = u.copy.desc.pixelSize().isEmpty() ? srcD->m_pixelSize : u.copy.desc.pixelSize();
+ const QPoint sp = u.copy.desc.sourceTopLeft();
+ D3D11_BOX srcBox;
+ srcBox.left = sp.x();
+ srcBox.top = sp.y();
+ srcBox.front = 0;
+ // back, right, bottom are exclusive
+ srcBox.right = srcBox.left + size.width();
+ srcBox.bottom = srcBox.top + size.height();
+ srcBox.back = 1;
+ QD3D11CommandBuffer::Command cmd;
+ cmd.cmd = QD3D11CommandBuffer::Command::CopySubRes;
+ cmd.args.copySubRes.dst = dstD->tex;
+ cmd.args.copySubRes.dstSubRes = dstSubRes;
+ cmd.args.copySubRes.dstX = dp.x();
+ cmd.args.copySubRes.dstY = dp.y();
+ cmd.args.copySubRes.src = srcD->tex;
+ cmd.args.copySubRes.srcSubRes = srcSubRes;
+ cmd.args.copySubRes.hasSrcBox = true;
+ cmd.args.copySubRes.srcBox = srcBox;
+ cbD->commands.append(cmd);
+ } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Read) {
+ ActiveReadback aRb;
+ aRb.desc = u.read.rb;
+ aRb.result = u.read.result;
+
+ ID3D11Resource *src;
+ DXGI_FORMAT dxgiFormat;
+ QSize pixelSize;
+ QRhiTexture::Format format;
+ UINT subres = 0;
+ QD3D11Texture *texD = QRHI_RES(QD3D11Texture, u.read.rb.texture());
+ QD3D11SwapChain *swapChainD = nullptr;
+
+ if (texD) {
+ if (texD->sampleDesc.Count > 1) {
+ qWarning("Multisample texture cannot be read back");
+ continue;
+ }
+ src = texD->tex;
+ dxgiFormat = texD->dxgiFormat;
+ pixelSize = u.read.rb.level() > 0 ? q->sizeForMipLevel(u.read.rb.level(), texD->m_pixelSize) : texD->m_pixelSize;
+ format = texD->m_format;
+ subres = D3D11CalcSubresource(u.read.rb.level(), u.read.rb.layer(), texD->mipLevelCount);
+ } else {
+ Q_ASSERT(contextState.currentSwapChain);
+ swapChainD = QRHI_RES(QD3D11SwapChain, contextState.currentSwapChain);
+ if (swapChainD->sampleDesc.Count > 1) {
+ // Unlike with textures, reading back a multisample swapchain image
+ // has to be supported. Insert a resolve.
+ QD3D11CommandBuffer::Command rcmd;
+ rcmd.cmd = QD3D11CommandBuffer::Command::ResolveSubRes;
+ rcmd.args.resolveSubRes.dst = swapChainD->tex[swapChainD->currentFrameSlot];
+ rcmd.args.resolveSubRes.dstSubRes = 0;
+ rcmd.args.resolveSubRes.src = swapChainD->msaaTex[swapChainD->currentFrameSlot];
+ rcmd.args.resolveSubRes.srcSubRes = 0;
+ rcmd.args.resolveSubRes.format = swapChainD->colorFormat;
+ cbD->commands.append(rcmd);
+ }
+ src = swapChainD->tex[swapChainD->currentFrameSlot];
+ dxgiFormat = swapChainD->colorFormat;
+ pixelSize = swapChainD->pixelSize;
+ format = colorTextureFormatFromDxgiFormat(dxgiFormat, nullptr);
+ if (format == QRhiTexture::UnknownFormat)
+ continue;
+ }
+ quint32 bufSize = 0;
+ quint32 bpl = 0;
+ textureFormatInfo(format, pixelSize, &bpl, &bufSize);
+
+ D3D11_TEXTURE2D_DESC desc;
+ memset(&desc, 0, sizeof(desc));
+ desc.Width = pixelSize.width();
+ desc.Height = pixelSize.height();
+ desc.MipLevels = 1;
+ desc.ArraySize = 1;
+ desc.Format = dxgiFormat;
+ desc.SampleDesc.Count = 1;
+ desc.Usage = D3D11_USAGE_STAGING;
+ desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
+ ID3D11Texture2D *stagingTex;
+ HRESULT hr = dev->CreateTexture2D(&desc, nullptr, &stagingTex);
+ if (FAILED(hr)) {
+ qWarning("Failed to create readback staging texture: %s", qPrintable(comErrorMessage(hr)));
+ return;
+ }
+ QRHI_PROF_F(newReadbackBuffer(quint64(quintptr(stagingTex)),
+ texD ? static_cast<QRhiResource *>(texD) : static_cast<QRhiResource *>(swapChainD),
+ bufSize));
+
+ QD3D11CommandBuffer::Command cmd;
+ cmd.cmd = QD3D11CommandBuffer::Command::CopySubRes;
+ cmd.args.copySubRes.dst = stagingTex;
+ cmd.args.copySubRes.dstSubRes = 0;
+ cmd.args.copySubRes.dstX = 0;
+ cmd.args.copySubRes.dstY = 0;
+ cmd.args.copySubRes.src = src;
+ cmd.args.copySubRes.srcSubRes = subres;
+ cmd.args.copySubRes.hasSrcBox = false;
+ cbD->commands.append(cmd);
+
+ aRb.stagingTex = stagingTex;
+ aRb.bufSize = bufSize;
+ aRb.bpl = bpl;
+ aRb.pixelSize = pixelSize;
+ aRb.format = format;
+
+ activeReadbacks.append(aRb);
+ } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::MipGen) {
+ Q_ASSERT(u.mipgen.tex->flags().testFlag(QRhiTexture::UsedWithGenerateMips));
+ QD3D11CommandBuffer::Command cmd;
+ cmd.cmd = QD3D11CommandBuffer::Command::GenMip;
+ cmd.args.genMip.srv = QRHI_RES(QD3D11Texture, u.mipgen.tex)->srv;
+ cbD->commands.append(cmd);
+ }
+ }
+
+ ud->free();
+}
+
+void QRhiD3D11::finishActiveReadbacks()
+{
+ QVarLengthArray<std::function<void()>, 4> completedCallbacks;
+ QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
+
+ for (int i = activeReadbacks.count() - 1; i >= 0; --i) {
+ const QRhiD3D11::ActiveReadback &aRb(activeReadbacks[i]);
+ aRb.result->format = aRb.format;
+ aRb.result->pixelSize = aRb.pixelSize;
+ aRb.result->data.resize(aRb.bufSize);
+
+ D3D11_MAPPED_SUBRESOURCE mp;
+ HRESULT hr = context->Map(aRb.stagingTex, 0, D3D11_MAP_READ, 0, &mp);
+ if (FAILED(hr)) {
+ qWarning("Failed to map readback staging texture: %s", qPrintable(comErrorMessage(hr)));
+ aRb.stagingTex->Release();
+ continue;
+ }
+ // nothing says the rows are tightly packed in the texture, must take
+ // the stride into account
+ char *dst = aRb.result->data.data();
+ char *src = static_cast<char *>(mp.pData);
+ for (int y = 0, h = aRb.pixelSize.height(); y != h; ++y) {
+ memcpy(dst, src, aRb.bpl);
+ dst += aRb.bpl;
+ src += mp.RowPitch;
+ }
+ context->Unmap(aRb.stagingTex, 0);
+
+ aRb.stagingTex->Release();
+ QRHI_PROF_F(releaseReadbackBuffer(quint64(quintptr(aRb.stagingTex))));
+
+ if (aRb.result->completed)
+ completedCallbacks.append(aRb.result->completed);
+
+ activeReadbacks.removeAt(i);
+ }
+
+ for (auto f : completedCallbacks)
+ f();
+}
+
+static inline QD3D11RenderTargetData *rtData(QRhiRenderTarget *rt)
+{
+ switch (rt->resourceType()) {
+ case QRhiResource::RenderTarget:
+ return &QRHI_RES(QD3D11ReferenceRenderTarget, rt)->d;
+ case QRhiResource::TextureRenderTarget:
+ return &QRHI_RES(QD3D11TextureRenderTarget, rt)->d;
+ default:
+ Q_UNREACHABLE();
+ return nullptr;
+ }
+}
+
+void QRhiD3D11::resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ Q_ASSERT(inFrame && !inPass);
+
+ enqueueResourceUpdates(cb, resourceUpdates);
+}
+
+void QRhiD3D11::enqueueSetRenderTarget(QD3D11CommandBuffer *cbD, QRhiRenderTarget *rt)
+{
+ QD3D11CommandBuffer::Command fbCmd;
+ fbCmd.cmd = QD3D11CommandBuffer::Command::SetRenderTarget;
+ fbCmd.args.setRenderTarget.rt = rt;
+ cbD->commands.append(fbCmd);
+}
+
+void QRhiD3D11::beginPass(QRhiCommandBuffer *cb,
+ QRhiRenderTarget *rt,
+ const QColor &colorClearValue,
+ const QRhiDepthStencilClearValue &depthStencilClearValue,
+ QRhiResourceUpdateBatch *resourceUpdates)
+{
+ Q_ASSERT(inFrame && !inPass);
+
+ if (resourceUpdates)
+ enqueueResourceUpdates(cb, resourceUpdates);
+
+ QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
+ bool wantsColorClear = true;
+ bool wantsDsClear = true;
+ QD3D11RenderTargetData *rtD = rtData(rt);
+ if (rt->resourceType() == QRhiRenderTarget::TextureRenderTarget) {
+ QD3D11TextureRenderTarget *rtTex = QRHI_RES(QD3D11TextureRenderTarget, rt);
+ wantsColorClear = !rtTex->m_flags.testFlag(QRhiTextureRenderTarget::PreserveColorContents);
+ wantsDsClear = !rtTex->m_flags.testFlag(QRhiTextureRenderTarget::PreserveDepthStencilContents);
+ }
+ enqueueSetRenderTarget(cbD, rt);
+
+ QD3D11CommandBuffer::Command clearCmd;
+ clearCmd.cmd = QD3D11CommandBuffer::Command::Clear;
+ clearCmd.args.clear.rt = rt;
+ clearCmd.args.clear.mask = 0;
+ if (rtD->colorAttCount && wantsColorClear)
+ clearCmd.args.clear.mask |= QD3D11CommandBuffer::Command::Color;
+ if (rtD->dsAttCount && wantsDsClear)
+ clearCmd.args.clear.mask |= QD3D11CommandBuffer::Command::Depth | QD3D11CommandBuffer::Command::Stencil;
+
+ clearCmd.args.clear.c[0] = colorClearValue.redF();
+ clearCmd.args.clear.c[1] = colorClearValue.greenF();
+ clearCmd.args.clear.c[2] = colorClearValue.blueF();
+ clearCmd.args.clear.c[3] = colorClearValue.alphaF();
+ clearCmd.args.clear.d = depthStencilClearValue.depthClearValue();
+ clearCmd.args.clear.s = depthStencilClearValue.stencilClearValue();
+ cbD->commands.append(clearCmd);
+
+ cbD->currentTarget = rt;
+
+ inPass = true;
+}
+
+void QRhiD3D11::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ Q_ASSERT(inPass);
+ inPass = false;
+
+ QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
+ if (cbD->currentTarget->resourceType() == QRhiResource::TextureRenderTarget) {
+ QD3D11TextureRenderTarget *rtTex = QRHI_RES(QD3D11TextureRenderTarget, cbD->currentTarget);
+ const QVector<QRhiColorAttachment> colorAttachments = rtTex->m_desc.colorAttachments();
+ for (int att = 0, attCount = colorAttachments.count(); att != attCount; ++att) {
+ const QRhiColorAttachment &colorAtt(colorAttachments[att]);
+ if (!colorAtt.resolveTexture())
+ continue;
+
+ QD3D11Texture *dstTexD = QRHI_RES(QD3D11Texture, colorAtt.resolveTexture());
+ QD3D11Texture *srcTexD = QRHI_RES(QD3D11Texture, colorAtt.texture());
+ QD3D11RenderBuffer *srcRbD = QRHI_RES(QD3D11RenderBuffer, colorAtt.renderBuffer());
+ Q_ASSERT(srcTexD || srcRbD);
+ QD3D11CommandBuffer::Command cmd;
+ cmd.cmd = QD3D11CommandBuffer::Command::ResolveSubRes;
+ cmd.args.resolveSubRes.dst = dstTexD->tex;
+ cmd.args.resolveSubRes.dstSubRes = D3D11CalcSubresource(colorAtt.resolveLevel(),
+ colorAtt.resolveLayer(),
+ dstTexD->mipLevelCount);
+ if (srcTexD) {
+ cmd.args.resolveSubRes.src = srcTexD->tex;
+ if (srcTexD->dxgiFormat != dstTexD->dxgiFormat) {
+ qWarning("Resolve source and destination formats do not match");
+ continue;
+ }
+ if (srcTexD->sampleDesc.Count <= 1) {
+ qWarning("Cannot resolve a non-multisample texture");
+ continue;
+ }
+ if (srcTexD->m_pixelSize != dstTexD->m_pixelSize) {
+ qWarning("Resolve source and destination sizes do not match");
+ continue;
+ }
+ } else {
+ cmd.args.resolveSubRes.src = srcRbD->tex;
+ if (srcRbD->dxgiFormat != dstTexD->dxgiFormat) {
+ qWarning("Resolve source and destination formats do not match");
+ continue;
+ }
+ if (srcRbD->m_pixelSize != dstTexD->m_pixelSize) {
+ qWarning("Resolve source and destination sizes do not match");
+ continue;
+ }
+ }
+ cmd.args.resolveSubRes.srcSubRes = D3D11CalcSubresource(0, colorAtt.layer(), 1);
+ cmd.args.resolveSubRes.format = dstTexD->dxgiFormat;
+ cbD->commands.append(cmd);
+ }
+ }
+
+ cbD->currentTarget = nullptr;
+
+ if (resourceUpdates)
+ enqueueResourceUpdates(cb, resourceUpdates);
+}
+
+void QRhiD3D11::updateShaderResourceBindings(QD3D11ShaderResourceBindings *srbD)
+{
+ srbD->vsubufs.clear();
+ srbD->vsubufoffsets.clear();
+ srbD->vsubufsizes.clear();
+
+ srbD->fsubufs.clear();
+ srbD->fsubufoffsets.clear();
+ srbD->fsubufsizes.clear();
+
+ srbD->vssamplers.clear();
+ srbD->vsshaderresources.clear();
+
+ srbD->fssamplers.clear();
+ srbD->fsshaderresources.clear();
+
+ for (int i = 0, ie = srbD->sortedBindings.count(); i != ie; ++i) {
+ const QRhiShaderResourceBindingPrivate *b = QRhiShaderResourceBindingPrivate::get(&srbD->sortedBindings[i]);
+ QD3D11ShaderResourceBindings::BoundResourceData &bd(srbD->boundResourceData[i]);
+ switch (b->type) {
+ case QRhiShaderResourceBinding::UniformBuffer:
+ {
+ QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, b->u.ubuf.buf);
+ Q_ASSERT(aligned(b->u.ubuf.offset, 256) == b->u.ubuf.offset);
+ bd.ubuf.id = bufD->m_id;
+ bd.ubuf.generation = bufD->generation;
+ // dynamic ubuf offsets are not considered here, those are baked in
+ // at a later stage, which is good as vsubufoffsets and friends are
+ // per-srb, not per-setShaderResources call
+ const uint offsetInConstants = b->u.ubuf.offset / 16;
+ // size must be 16 mult. (in constants, i.e. multiple of 256 bytes).
+ // We can round up if needed since the buffers's actual size
+ // (ByteWidth) is always a multiple of 256.
+ const uint sizeInConstants = aligned(b->u.ubuf.maybeSize ? b->u.ubuf.maybeSize : bufD->m_size, 256) / 16;
+ if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) {
+ srbD->vsubufs.feed(b->binding, bufD->buffer);
+ srbD->vsubufoffsets.feed(b->binding, offsetInConstants);
+ srbD->vsubufsizes.feed(b->binding, sizeInConstants);
+ }
+ if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
+ srbD->fsubufs.feed(b->binding, bufD->buffer);
+ srbD->fsubufoffsets.feed(b->binding, offsetInConstants);
+ srbD->fsubufsizes.feed(b->binding, sizeInConstants);
+ }
+ }
+ break;
+ case QRhiShaderResourceBinding::SampledTexture:
+ {
+ // A sampler with binding N is mapped to a HLSL sampler and texture
+ // with registers sN and tN by SPIRV-Cross.
+ QD3D11Texture *texD = QRHI_RES(QD3D11Texture, b->u.stex.tex);
+ QD3D11Sampler *samplerD = QRHI_RES(QD3D11Sampler, b->u.stex.sampler);
+ bd.stex.texId = texD->m_id;
+ bd.stex.texGeneration = texD->generation;
+ bd.stex.samplerId = samplerD->m_id;
+ bd.stex.samplerGeneration = samplerD->generation;
+ if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) {
+ srbD->vssamplers.feed(b->binding, samplerD->samplerState);
+ srbD->vsshaderresources.feed(b->binding, texD->srv);
+ }
+ if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
+ srbD->fssamplers.feed(b->binding, samplerD->samplerState);
+ srbD->fsshaderresources.feed(b->binding, texD->srv);
+ }
+ }
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ }
+
+ srbD->vsubufs.finish();
+ srbD->vsubufoffsets.finish();
+ srbD->vsubufsizes.finish();
+
+ srbD->fsubufs.finish();
+ srbD->fsubufoffsets.finish();
+ srbD->fsubufsizes.finish();
+
+ srbD->vssamplers.finish();
+ srbD->vsshaderresources.finish();
+
+ srbD->fssamplers.finish();
+ srbD->fsshaderresources.finish();
+}
+
+void QRhiD3D11::executeBufferHostWritesForCurrentFrame(QD3D11Buffer *bufD)
+{
+ if (!bufD->hasPendingDynamicUpdates)
+ return;
+
+ Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic);
+ bufD->hasPendingDynamicUpdates = false;
+ D3D11_MAPPED_SUBRESOURCE mp;
+ HRESULT hr = context->Map(bufD->buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mp);
+ if (SUCCEEDED(hr)) {
+ memcpy(mp.pData, bufD->dynBuf.constData(), bufD->dynBuf.size());
+ context->Unmap(bufD->buffer, 0);
+ } else {
+ qWarning("Failed to map buffer: %s", qPrintable(comErrorMessage(hr)));
+ }
+}
+
+void QRhiD3D11::bindShaderResources(QD3D11ShaderResourceBindings *srbD,
+ const uint *dynOfsPairs, int dynOfsPairCount,
+ bool offsetOnlyChange)
+{
+ if (!offsetOnlyChange) {
+ for (const auto &batch : srbD->vssamplers.batches)
+ context->VSSetSamplers(batch.startBinding, batch.resources.count(), batch.resources.constData());
+
+ for (const auto &batch : srbD->vsshaderresources.batches) {
+ context->VSSetShaderResources(batch.startBinding, batch.resources.count(), batch.resources.constData());
+ contextState.vsHighestActiveSrvBinding = qMax<int>(contextState.vsHighestActiveSrvBinding,
+ batch.startBinding + batch.resources.count() - 1);
+ }
+
+ for (const auto &batch : srbD->fssamplers.batches)
+ context->PSSetSamplers(batch.startBinding, batch.resources.count(), batch.resources.constData());
+
+ for (const auto &batch : srbD->fsshaderresources.batches) {
+ context->PSSetShaderResources(batch.startBinding, batch.resources.count(), batch.resources.constData());
+ contextState.fsHighestActiveSrvBinding = qMax<int>(contextState.fsHighestActiveSrvBinding,
+ batch.startBinding + batch.resources.count() - 1);
+ }
+ }
+
+ for (int i = 0, ie = srbD->vsubufs.batches.count(); i != ie; ++i) {
+ if (!dynOfsPairCount) {
+ context->VSSetConstantBuffers1(srbD->vsubufs.batches[i].startBinding,
+ srbD->vsubufs.batches[i].resources.count(),
+ srbD->vsubufs.batches[i].resources.constData(),
+ srbD->vsubufoffsets.batches[i].resources.constData(),
+ srbD->vsubufsizes.batches[i].resources.constData());
+ } else {
+ const UINT count = srbD->vsubufs.batches[i].resources.count();
+ const UINT startBinding = srbD->vsubufs.batches[i].startBinding;
+ QVarLengthArray<UINT, 4> offsets = srbD->vsubufoffsets.batches[i].resources;
+ for (UINT b = 0; b < count; ++b) {
+ for (int di = 0; di < dynOfsPairCount; ++di) {
+ const uint binding = dynOfsPairs[2 * di];
+ if (binding == startBinding + b) {
+ const uint offsetInConstants = dynOfsPairs[2 * di + 1];
+ offsets[b] = offsetInConstants;
+ break;
+ }
+ }
+ }
+ context->VSSetConstantBuffers1(startBinding,
+ count,
+ srbD->vsubufs.batches[i].resources.constData(),
+ offsets.constData(),
+ srbD->vsubufsizes.batches[i].resources.constData());
+ }
+ }
+
+ for (int i = 0, ie = srbD->fsubufs.batches.count(); i != ie; ++i) {
+ if (!dynOfsPairCount) {
+ context->PSSetConstantBuffers1(srbD->fsubufs.batches[i].startBinding,
+ srbD->fsubufs.batches[i].resources.count(),
+ srbD->fsubufs.batches[i].resources.constData(),
+ srbD->fsubufoffsets.batches[i].resources.constData(),
+ srbD->fsubufsizes.batches[i].resources.constData());
+ } else {
+ const UINT count = srbD->fsubufs.batches[i].resources.count();
+ const UINT startBinding = srbD->fsubufs.batches[i].startBinding;
+ QVarLengthArray<UINT, 4> offsets = srbD->fsubufoffsets.batches[i].resources;
+ for (UINT b = 0; b < count; ++b) {
+ for (int di = 0; di < dynOfsPairCount; ++di) {
+ const uint binding = dynOfsPairs[2 * di];
+ if (binding == startBinding + b) {
+ const uint offsetInConstants = dynOfsPairs[2 * di + 1];
+ offsets[b] = offsetInConstants;
+ break;
+ }
+ }
+ }
+ context->PSSetConstantBuffers1(startBinding,
+ count,
+ srbD->fsubufs.batches[i].resources.constData(),
+ offsets.constData(),
+ srbD->fsubufsizes.batches[i].resources.constData());
+ }
+ }
+}
+
+void QRhiD3D11::setRenderTarget(QRhiRenderTarget *rt)
+{
+ // The new output cannot be bound as input from the previous frame,
+ // otherwise the debug layer complains. Avoid this.
+ const int nullsrvCount = qMax(contextState.vsHighestActiveSrvBinding, contextState.fsHighestActiveSrvBinding) + 1;
+ if (nullsrvCount > 0) {
+ QVarLengthArray<ID3D11ShaderResourceView *,
+ D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT> nullsrvs(nullsrvCount);
+ for (int i = 0; i < nullsrvs.count(); ++i)
+ nullsrvs[i] = nullptr;
+ if (contextState.vsHighestActiveSrvBinding >= 0) {
+ context->VSSetShaderResources(0, contextState.vsHighestActiveSrvBinding + 1, nullsrvs.constData());
+ contextState.vsHighestActiveSrvBinding = -1;
+ }
+ if (contextState.fsHighestActiveSrvBinding >= 0) {
+ context->PSSetShaderResources(0, contextState.fsHighestActiveSrvBinding + 1, nullsrvs.constData());
+ contextState.fsHighestActiveSrvBinding = -1;
+ }
+ }
+ QD3D11RenderTargetData *rtD = rtData(rt);
+ context->OMSetRenderTargets(rtD->colorAttCount, rtD->colorAttCount ? rtD->rtv : nullptr, rtD->dsv);
+}
+
+void QRhiD3D11::executeCommandBuffer(QD3D11CommandBuffer *cbD, QD3D11SwapChain *timestampSwapChain)
+{
+ quint32 stencilRef = 0;
+ float blendConstants[] = { 1, 1, 1, 1 };
+
+ if (timestampSwapChain) {
+ const int currentFrameSlot = timestampSwapChain->currentFrameSlot;
+ ID3D11Query *tsDisjoint = timestampSwapChain->timestampDisjointQuery[currentFrameSlot];
+ const int tsIdx = QD3D11SwapChain::BUFFER_COUNT * currentFrameSlot;
+ ID3D11Query *tsStart = timestampSwapChain->timestampQuery[tsIdx];
+ if (tsDisjoint && tsStart && !timestampSwapChain->timestampActive[currentFrameSlot]) {
+ // The timestamps seem to include vsync time with Present(1), except
+ // when running on a non-primary gpu. This is not ideal. So try working
+ // it around by issuing a semi-fake OMSetRenderTargets early and
+ // writing the first timestamp only afterwards.
+ context->Begin(tsDisjoint);
+ setRenderTarget(&timestampSwapChain->rt);
+ context->End(tsStart); // just record a timestamp, no Begin needed
+ }
+ }
+
+ for (const QD3D11CommandBuffer::Command &cmd : qAsConst(cbD->commands)) {
+ switch (cmd.cmd) {
+ case QD3D11CommandBuffer::Command::SetRenderTarget:
+ setRenderTarget(cmd.args.setRenderTarget.rt);
+ break;
+ case QD3D11CommandBuffer::Command::Clear:
+ {
+ QD3D11RenderTargetData *rtD = rtData(cmd.args.clear.rt);
+ if (cmd.args.clear.mask & QD3D11CommandBuffer::Command::Color) {
+ for (int i = 0; i < rtD->colorAttCount; ++i)
+ context->ClearRenderTargetView(rtD->rtv[i], cmd.args.clear.c);
+ }
+ uint ds = 0;
+ if (cmd.args.clear.mask & QD3D11CommandBuffer::Command::Depth)
+ ds |= D3D11_CLEAR_DEPTH;
+ if (cmd.args.clear.mask & QD3D11CommandBuffer::Command::Stencil)
+ ds |= D3D11_CLEAR_STENCIL;
+ if (ds)
+ context->ClearDepthStencilView(rtD->dsv, ds, cmd.args.clear.d, cmd.args.clear.s);
+ }
+ break;
+ case QD3D11CommandBuffer::Command::Viewport:
+ {
+ D3D11_VIEWPORT v;
+ v.TopLeftX = cmd.args.viewport.x;
+ v.TopLeftY = cmd.args.viewport.y;
+ v.Width = cmd.args.viewport.w;
+ v.Height = cmd.args.viewport.h;
+ v.MinDepth = cmd.args.viewport.d0;
+ v.MaxDepth = cmd.args.viewport.d1;
+ context->RSSetViewports(1, &v);
+ }
+ break;
+ case QD3D11CommandBuffer::Command::Scissor:
+ {
+ D3D11_RECT r;
+ r.left = cmd.args.scissor.x;
+ r.top = cmd.args.scissor.y;
+ // right and bottom are exclusive
+ r.right = cmd.args.scissor.x + cmd.args.scissor.w;
+ r.bottom = cmd.args.scissor.y + cmd.args.scissor.h;
+ context->RSSetScissorRects(1, &r);
+ }
+ break;
+ case QD3D11CommandBuffer::Command::BindVertexBuffers:
+ context->IASetVertexBuffers(cmd.args.bindVertexBuffers.startSlot,
+ cmd.args.bindVertexBuffers.slotCount,
+ cmd.args.bindVertexBuffers.buffers,
+ cmd.args.bindVertexBuffers.strides,
+ cmd.args.bindVertexBuffers.offsets);
+ break;
+ case QD3D11CommandBuffer::Command::BindIndexBuffer:
+ context->IASetIndexBuffer(cmd.args.bindIndexBuffer.buffer,
+ cmd.args.bindIndexBuffer.format,
+ cmd.args.bindIndexBuffer.offset);
+ break;
+ case QD3D11CommandBuffer::Command::BindGraphicsPipeline:
+ {
+ QD3D11GraphicsPipeline *psD = cmd.args.bindGraphicsPipeline.ps;
+ context->VSSetShader(psD->vs, nullptr, 0);
+ context->PSSetShader(psD->fs, nullptr, 0);
+ context->IASetPrimitiveTopology(psD->d3dTopology);
+ context->IASetInputLayout(psD->inputLayout);
+ context->OMSetDepthStencilState(psD->dsState, stencilRef);
+ context->OMSetBlendState(psD->blendState, blendConstants, 0xffffffff);
+ context->RSSetState(psD->rastState);
+ }
+ break;
+ case QD3D11CommandBuffer::Command::BindShaderResources:
+ bindShaderResources(cmd.args.bindShaderResources.srb,
+ cmd.args.bindShaderResources.dynamicOffsetPairs,
+ cmd.args.bindShaderResources.dynamicOffsetCount,
+ cmd.args.bindShaderResources.offsetOnlyChange);
+ break;
+ case QD3D11CommandBuffer::Command::StencilRef:
+ stencilRef = cmd.args.stencilRef.ref;
+ context->OMSetDepthStencilState(cmd.args.stencilRef.ps->dsState, stencilRef);
+ break;
+ case QD3D11CommandBuffer::Command::BlendConstants:
+ memcpy(blendConstants, cmd.args.blendConstants.c, 4 * sizeof(float));
+ context->OMSetBlendState(cmd.args.blendConstants.ps->blendState, blendConstants, 0xffffffff);
+ break;
+ case QD3D11CommandBuffer::Command::Draw:
+ if (cmd.args.draw.ps) {
+ if (cmd.args.draw.instanceCount == 1)
+ context->Draw(cmd.args.draw.vertexCount, cmd.args.draw.firstVertex);
+ else
+ context->DrawInstanced(cmd.args.draw.vertexCount, cmd.args.draw.instanceCount,
+ cmd.args.draw.firstVertex, cmd.args.draw.firstInstance);
+ } else {
+ qWarning("No graphics pipeline active for draw; ignored");
+ }
+ break;
+ case QD3D11CommandBuffer::Command::DrawIndexed:
+ if (cmd.args.drawIndexed.ps) {
+ if (cmd.args.drawIndexed.instanceCount == 1)
+ context->DrawIndexed(cmd.args.drawIndexed.indexCount, cmd.args.drawIndexed.firstIndex,
+ cmd.args.drawIndexed.vertexOffset);
+ else
+ context->DrawIndexedInstanced(cmd.args.drawIndexed.indexCount, cmd.args.drawIndexed.instanceCount,
+ cmd.args.drawIndexed.firstIndex, cmd.args.drawIndexed.vertexOffset,
+ cmd.args.drawIndexed.firstInstance);
+ } else {
+ qWarning("No graphics pipeline active for drawIndexed; ignored");
+ }
+ break;
+ case QD3D11CommandBuffer::Command::UpdateSubRes:
+ context->UpdateSubresource(cmd.args.updateSubRes.dst, cmd.args.updateSubRes.dstSubRes,
+ cmd.args.updateSubRes.hasDstBox ? &cmd.args.updateSubRes.dstBox : nullptr,
+ cmd.args.updateSubRes.src, cmd.args.updateSubRes.srcRowPitch, 0);
+ break;
+ case QD3D11CommandBuffer::Command::CopySubRes:
+ context->CopySubresourceRegion(cmd.args.copySubRes.dst, cmd.args.copySubRes.dstSubRes,
+ cmd.args.copySubRes.dstX, cmd.args.copySubRes.dstY, 0,
+ cmd.args.copySubRes.src, cmd.args.copySubRes.srcSubRes,
+ cmd.args.copySubRes.hasSrcBox ? &cmd.args.copySubRes.srcBox : nullptr);
+ break;
+ case QD3D11CommandBuffer::Command::ResolveSubRes:
+ context->ResolveSubresource(cmd.args.resolveSubRes.dst, cmd.args.resolveSubRes.dstSubRes,
+ cmd.args.resolveSubRes.src, cmd.args.resolveSubRes.srcSubRes,
+ cmd.args.resolveSubRes.format);
+ break;
+ case QD3D11CommandBuffer::Command::GenMip:
+ context->GenerateMips(cmd.args.genMip.srv);
+ break;
+ case QD3D11CommandBuffer::Command::DebugMarkBegin:
+ annotations->BeginEvent(reinterpret_cast<LPCWSTR>(QString::fromLatin1(cmd.args.debugMark.s).utf16()));
+ break;
+ case QD3D11CommandBuffer::Command::DebugMarkEnd:
+ annotations->EndEvent();
+ break;
+ case QD3D11CommandBuffer::Command::DebugMarkMsg:
+ annotations->SetMarker(reinterpret_cast<LPCWSTR>(QString::fromLatin1(cmd.args.debugMark.s).utf16()));
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+QD3D11Buffer::QD3D11Buffer(QRhiImplementation *rhi, Type type, UsageFlags usage, int size)
+ : QRhiBuffer(rhi, type, usage, size)
+{
+}
+
+QD3D11Buffer::~QD3D11Buffer()
+{
+ release();
+}
+
+void QD3D11Buffer::release()
+{
+ if (!buffer)
+ return;
+
+ dynBuf.clear();
+
+ buffer->Release();
+ buffer = nullptr;
+
+ QRHI_RES_RHI(QRhiD3D11);
+ QRHI_PROF;
+ QRHI_PROF_F(releaseBuffer(this));
+ rhiD->unregisterResource(this);
+}
+
+static inline uint toD3DBufferUsage(QRhiBuffer::UsageFlags usage)
+{
+ int u = 0;
+ if (usage.testFlag(QRhiBuffer::VertexBuffer))
+ u |= D3D11_BIND_VERTEX_BUFFER;
+ if (usage.testFlag(QRhiBuffer::IndexBuffer))
+ u |= D3D11_BIND_INDEX_BUFFER;
+ if (usage.testFlag(QRhiBuffer::UniformBuffer))
+ u |= D3D11_BIND_CONSTANT_BUFFER;
+ return u;
+}
+
+bool QD3D11Buffer::build()
+{
+ if (buffer)
+ release();
+
+ const int nonZeroSize = m_size <= 0 ? 256 : m_size;
+ const int roundedSize = m_usage.testFlag(QRhiBuffer::UniformBuffer) ? aligned(nonZeroSize, 256) : nonZeroSize;
+
+ D3D11_BUFFER_DESC desc;
+ memset(&desc, 0, sizeof(desc));
+ desc.ByteWidth = roundedSize;
+ desc.Usage = m_type == Dynamic ? D3D11_USAGE_DYNAMIC : D3D11_USAGE_DEFAULT;
+ desc.BindFlags = toD3DBufferUsage(m_usage);
+ desc.CPUAccessFlags = m_type == Dynamic ? D3D11_CPU_ACCESS_WRITE : 0;
+
+ QRHI_RES_RHI(QRhiD3D11);
+ HRESULT hr = rhiD->dev->CreateBuffer(&desc, nullptr, &buffer);
+ if (FAILED(hr)) {
+ qWarning("Failed to create buffer: %s", qPrintable(comErrorMessage(hr)));
+ return false;
+ }
+
+ if (m_type == Dynamic) {
+ dynBuf.resize(m_size);
+ hasPendingDynamicUpdates = false;
+ }
+
+ if (!m_objectName.isEmpty())
+ buffer->SetPrivateData(WKPDID_D3DDebugObjectName, m_objectName.size(), m_objectName.constData());
+
+ QRHI_PROF;
+ QRHI_PROF_F(newBuffer(this, roundedSize, m_type == Dynamic ? 2 : 1, m_type == Dynamic ? 1 : 0));
+
+ generation += 1;
+ rhiD->registerResource(this);
+ return true;
+}
+
+QD3D11RenderBuffer::QD3D11RenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize,
+ int sampleCount, QRhiRenderBuffer::Flags flags)
+ : QRhiRenderBuffer(rhi, type, pixelSize, sampleCount, flags)
+{
+}
+
+QD3D11RenderBuffer::~QD3D11RenderBuffer()
+{
+ release();
+}
+
+void QD3D11RenderBuffer::release()
+{
+ if (!tex)
+ return;
+
+ if (dsv) {
+ dsv->Release();
+ dsv = nullptr;
+ }
+
+ if (rtv) {
+ rtv->Release();
+ rtv = nullptr;
+ }
+
+ tex->Release();
+ tex = nullptr;
+
+ QRHI_RES_RHI(QRhiD3D11);
+ QRHI_PROF;
+ QRHI_PROF_F(releaseRenderBuffer(this));
+ rhiD->unregisterResource(this);
+}
+
+bool QD3D11RenderBuffer::build()
+{
+ if (tex)
+ release();
+
+ if (m_pixelSize.isEmpty())
+ return false;
+
+ QRHI_RES_RHI(QRhiD3D11);
+ sampleDesc = rhiD->effectiveSampleCount(m_sampleCount);
+
+ D3D11_TEXTURE2D_DESC desc;
+ memset(&desc, 0, sizeof(desc));
+ desc.Width = m_pixelSize.width();
+ desc.Height = m_pixelSize.height();
+ desc.MipLevels = 1;
+ desc.ArraySize = 1;
+ desc.SampleDesc = sampleDesc;
+ desc.Usage = D3D11_USAGE_DEFAULT;
+
+ if (m_type == Color) {
+ dxgiFormat = DXGI_FORMAT_R8G8B8A8_UNORM;
+ desc.Format = dxgiFormat;
+ desc.BindFlags = D3D11_BIND_RENDER_TARGET;
+ HRESULT hr = rhiD->dev->CreateTexture2D(&desc, nullptr, &tex);
+ if (FAILED(hr)) {
+ qWarning("Failed to create color renderbuffer: %s", qPrintable(comErrorMessage(hr)));
+ return false;
+ }
+ D3D11_RENDER_TARGET_VIEW_DESC rtvDesc;
+ memset(&rtvDesc, 0, sizeof(rtvDesc));
+ rtvDesc.Format = dxgiFormat;
+ rtvDesc.ViewDimension = desc.SampleDesc.Count > 1 ? D3D11_RTV_DIMENSION_TEXTURE2DMS
+ : D3D11_RTV_DIMENSION_TEXTURE2D;
+ hr = rhiD->dev->CreateRenderTargetView(tex, &rtvDesc, &rtv);
+ if (FAILED(hr)) {
+ qWarning("Failed to create rtv: %s", qPrintable(comErrorMessage(hr)));
+ return false;
+ }
+ } else if (m_type == DepthStencil) {
+ dxgiFormat = DXGI_FORMAT_D24_UNORM_S8_UINT;
+ desc.Format = dxgiFormat;
+ desc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
+ HRESULT hr = rhiD->dev->CreateTexture2D(&desc, nullptr, &tex);
+ if (FAILED(hr)) {
+ qWarning("Failed to create depth-stencil buffer: %s", qPrintable(comErrorMessage(hr)));
+ return false;
+ }
+ D3D11_DEPTH_STENCIL_VIEW_DESC dsvDesc;
+ memset(&dsvDesc, 0, sizeof(dsvDesc));
+ dsvDesc.Format = dxgiFormat;
+ dsvDesc.ViewDimension = desc.SampleDesc.Count > 1 ? D3D11_DSV_DIMENSION_TEXTURE2DMS
+ : D3D11_DSV_DIMENSION_TEXTURE2D;
+ hr = rhiD->dev->CreateDepthStencilView(tex, &dsvDesc, &dsv);
+ if (FAILED(hr)) {
+ qWarning("Failed to create dsv: %s", qPrintable(comErrorMessage(hr)));
+ return false;
+ }
+ } else {
+ return false;
+ }
+
+ if (!m_objectName.isEmpty())
+ tex->SetPrivateData(WKPDID_D3DDebugObjectName, m_objectName.size(), m_objectName.constData());
+
+ QRHI_PROF;
+ QRHI_PROF_F(newRenderBuffer(this, false, false, sampleDesc.Count));
+
+ rhiD->registerResource(this);
+ return true;
+}
+
+QRhiTexture::Format QD3D11RenderBuffer::backingFormat() const
+{
+ return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat;
+}
+
+QD3D11Texture::QD3D11Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize,
+ int sampleCount, Flags flags)
+ : QRhiTexture(rhi, format, pixelSize, sampleCount, flags)
+{
+}
+
+QD3D11Texture::~QD3D11Texture()
+{
+ release();
+}
+
+void QD3D11Texture::release()
+{
+ if (!tex)
+ return;
+
+ if (srv) {
+ srv->Release();
+ srv = nullptr;
+ }
+
+ if (owns)
+ tex->Release();
+
+ tex = nullptr;
+
+ QRHI_RES_RHI(QRhiD3D11);
+ QRHI_PROF;
+ QRHI_PROF_F(releaseTexture(this));
+ rhiD->unregisterResource(this);
+}
+
+static inline DXGI_FORMAT toD3DDepthTextureSRVFormat(QRhiTexture::Format format)
+{
+ switch (format) {
+ case QRhiTexture::Format::D16:
+ return DXGI_FORMAT_R16_FLOAT;
+ case QRhiTexture::Format::D32F:
+ return DXGI_FORMAT_R32_FLOAT;
+ default:
+ Q_UNREACHABLE();
+ return DXGI_FORMAT_R32_FLOAT;
+ }
+}
+
+static inline DXGI_FORMAT toD3DDepthTextureDSVFormat(QRhiTexture::Format format)
+{
+ switch (format) {
+ case QRhiTexture::Format::D16:
+ return DXGI_FORMAT_D16_UNORM;
+ case QRhiTexture::Format::D32F:
+ return DXGI_FORMAT_D32_FLOAT;
+ default:
+ Q_UNREACHABLE();
+ return DXGI_FORMAT_D32_FLOAT;
+ }
+}
+
+bool QD3D11Texture::prepareBuild(QSize *adjustedSize)
+{
+ if (tex)
+ release();
+
+ const QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize;
+ const bool isDepth = isDepthTextureFormat(m_format);
+ const bool isCube = m_flags.testFlag(CubeMap);
+ const bool hasMipMaps = m_flags.testFlag(MipMapped);
+
+ QRHI_RES_RHI(QRhiD3D11);
+ dxgiFormat = toD3DTextureFormat(m_format, m_flags);
+ mipLevelCount = hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1;
+ sampleDesc = rhiD->effectiveSampleCount(m_sampleCount);
+ if (sampleDesc.Count > 1) {
+ if (isCube) {
+ qWarning("Cubemap texture cannot be multisample");
+ return false;
+ }
+ if (hasMipMaps) {
+ qWarning("Multisample texture cannot have mipmaps");
+ return false;
+ }
+ }
+ if (isDepth && hasMipMaps) {
+ qWarning("Depth texture cannot have mipmaps");
+ return false;
+ }
+
+ if (adjustedSize)
+ *adjustedSize = size;
+
+ return true;
+}
+
+bool QD3D11Texture::finishBuild()
+{
+ QRHI_RES_RHI(QRhiD3D11);
+ const bool isDepth = isDepthTextureFormat(m_format);
+ const bool isCube = m_flags.testFlag(CubeMap);
+
+ D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
+ memset(&srvDesc, 0, sizeof(srvDesc));
+ srvDesc.Format = isDepth ? toD3DDepthTextureSRVFormat(m_format) : dxgiFormat;
+ if (isCube) {
+ srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE;
+ srvDesc.TextureCube.MipLevels = mipLevelCount;
+ } else {
+ if (sampleDesc.Count > 1) {
+ srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DMS;
+ } else {
+ srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
+ srvDesc.Texture2D.MipLevels = mipLevelCount;
+ }
+ }
+
+ HRESULT hr = rhiD->dev->CreateShaderResourceView(tex, &srvDesc, &srv);
+ if (FAILED(hr)) {
+ qWarning("Failed to create srv: %s", qPrintable(comErrorMessage(hr)));
+ return false;
+ }
+
+ nativeHandlesStruct.texture = tex;
+
+ generation += 1;
+ return true;
+}
+
+bool QD3D11Texture::build()
+{
+ QSize size;
+ if (!prepareBuild(&size))
+ return false;
+
+ const bool isDepth = isDepthTextureFormat(m_format);
+ const bool isCube = m_flags.testFlag(CubeMap);
+
+ uint bindFlags = D3D11_BIND_SHADER_RESOURCE;
+ uint miscFlags = isCube ? D3D11_RESOURCE_MISC_TEXTURECUBE : 0;
+ if (m_flags.testFlag(RenderTarget)) {
+ if (isDepth)
+ bindFlags |= D3D11_BIND_DEPTH_STENCIL;
+ else
+ bindFlags |= D3D11_BIND_RENDER_TARGET;
+ }
+ if (m_flags.testFlag(UsedWithGenerateMips)) {
+ if (isDepth) {
+ qWarning("Depth texture cannot have mipmaps generated");
+ return false;
+ }
+ bindFlags |= D3D11_BIND_RENDER_TARGET;
+ miscFlags |= D3D11_RESOURCE_MISC_GENERATE_MIPS;
+ }
+
+ D3D11_TEXTURE2D_DESC desc;
+ memset(&desc, 0, sizeof(desc));
+ desc.Width = size.width();
+ desc.Height = size.height();
+ desc.MipLevels = mipLevelCount;
+ desc.ArraySize = isCube ? 6 : 1;
+ desc.Format = dxgiFormat;
+ desc.SampleDesc = sampleDesc;
+ desc.Usage = D3D11_USAGE_DEFAULT;
+ desc.BindFlags = bindFlags;
+ desc.MiscFlags = miscFlags;
+
+ QRHI_RES_RHI(QRhiD3D11);
+ HRESULT hr = rhiD->dev->CreateTexture2D(&desc, nullptr, &tex);
+ if (FAILED(hr)) {
+ qWarning("Failed to create texture: %s", qPrintable(comErrorMessage(hr)));
+ return false;
+ }
+
+ if (!finishBuild())
+ return false;
+
+ if (!m_objectName.isEmpty())
+ tex->SetPrivateData(WKPDID_D3DDebugObjectName, m_objectName.size(), m_objectName.constData());
+
+ QRHI_PROF;
+ QRHI_PROF_F(newTexture(this, true, mipLevelCount, isCube ? 6 : 1, sampleDesc.Count));
+
+ owns = true;
+ rhiD->registerResource(this);
+ return true;
+}
+
+bool QD3D11Texture::buildFrom(const QRhiNativeHandles *src)
+{
+ const QRhiD3D11TextureNativeHandles *h = static_cast<const QRhiD3D11TextureNativeHandles *>(src);
+ if (!h || !h->texture)
+ return false;
+
+ if (!prepareBuild())
+ return false;
+
+ tex = static_cast<ID3D11Texture2D *>(h->texture);
+
+ if (!finishBuild())
+ return false;
+
+ QRHI_PROF;
+ QRHI_PROF_F(newTexture(this, false, mipLevelCount, m_flags.testFlag(CubeMap) ? 6 : 1, sampleDesc.Count));
+
+ owns = false;
+ QRHI_RES_RHI(QRhiD3D11);
+ rhiD->registerResource(this);
+ return true;
+}
+
+const QRhiNativeHandles *QD3D11Texture::nativeHandles()
+{
+ return &nativeHandlesStruct;
+}
+
+QD3D11Sampler::QD3D11Sampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode,
+ AddressMode u, AddressMode v)
+ : QRhiSampler(rhi, magFilter, minFilter, mipmapMode, u, v)
+{
+}
+
+QD3D11Sampler::~QD3D11Sampler()
+{
+ release();
+}
+
+void QD3D11Sampler::release()
+{
+ if (!samplerState)
+ return;
+
+ samplerState->Release();
+ samplerState = nullptr;
+
+ QRHI_RES_RHI(QRhiD3D11);
+ rhiD->unregisterResource(this);
+}
+
+static inline D3D11_FILTER toD3DFilter(QRhiSampler::Filter minFilter, QRhiSampler::Filter magFilter, QRhiSampler::Filter mipFilter)
+{
+ if (minFilter == QRhiSampler::Nearest) {
+ if (magFilter == QRhiSampler::Nearest) {
+ if (mipFilter == QRhiSampler::Linear)
+ return D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR;
+ else
+ return D3D11_FILTER_MIN_MAG_MIP_POINT;
+ } else {
+ if (mipFilter == QRhiSampler::Linear)
+ return D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR;
+ else
+ return D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT;
+ }
+ } else {
+ if (magFilter == QRhiSampler::Nearest) {
+ if (mipFilter == QRhiSampler::Linear)
+ return D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR;
+ else
+ return D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT;
+ } else {
+ if (mipFilter == QRhiSampler::Linear)
+ return D3D11_FILTER_MIN_MAG_MIP_LINEAR;
+ else
+ return D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT;
+ }
+ }
+
+ Q_UNREACHABLE();
+ return D3D11_FILTER_MIN_MAG_MIP_LINEAR;
+}
+
+static inline D3D11_TEXTURE_ADDRESS_MODE toD3DAddressMode(QRhiSampler::AddressMode m)
+{
+ switch (m) {
+ case QRhiSampler::Repeat:
+ return D3D11_TEXTURE_ADDRESS_WRAP;
+ case QRhiSampler::ClampToEdge:
+ return D3D11_TEXTURE_ADDRESS_CLAMP;
+ case QRhiSampler::Border:
+ return D3D11_TEXTURE_ADDRESS_BORDER;
+ case QRhiSampler::Mirror:
+ return D3D11_TEXTURE_ADDRESS_MIRROR;
+ case QRhiSampler::MirrorOnce:
+ return D3D11_TEXTURE_ADDRESS_MIRROR_ONCE;
+ default:
+ Q_UNREACHABLE();
+ return D3D11_TEXTURE_ADDRESS_CLAMP;
+ }
+}
+
+static inline D3D11_COMPARISON_FUNC toD3DTextureComparisonFunc(QRhiSampler::CompareOp op)
+{
+ switch (op) {
+ case QRhiSampler::Never:
+ return D3D11_COMPARISON_NEVER;
+ case QRhiSampler::Less:
+ return D3D11_COMPARISON_LESS;
+ case QRhiSampler::Equal:
+ return D3D11_COMPARISON_EQUAL;
+ case QRhiSampler::LessOrEqual:
+ return D3D11_COMPARISON_LESS_EQUAL;
+ case QRhiSampler::Greater:
+ return D3D11_COMPARISON_GREATER;
+ case QRhiSampler::NotEqual:
+ return D3D11_COMPARISON_NOT_EQUAL;
+ case QRhiSampler::GreaterOrEqual:
+ return D3D11_COMPARISON_GREATER_EQUAL;
+ case QRhiSampler::Always:
+ return D3D11_COMPARISON_ALWAYS;
+ default:
+ Q_UNREACHABLE();
+ return D3D11_COMPARISON_NEVER;
+ }
+}
+
+bool QD3D11Sampler::build()
+{
+ if (samplerState)
+ release();
+
+ D3D11_SAMPLER_DESC desc;
+ memset(&desc, 0, sizeof(desc));
+ desc.Filter = toD3DFilter(m_minFilter, m_magFilter, m_mipmapMode);
+ if (m_compareOp != Never)
+ desc.Filter = D3D11_FILTER(desc.Filter | 0x80);
+ desc.AddressU = toD3DAddressMode(m_addressU);
+ desc.AddressV = toD3DAddressMode(m_addressV);
+ desc.AddressW = toD3DAddressMode(m_addressW);
+ desc.MaxAnisotropy = 1.0f;
+ desc.ComparisonFunc = toD3DTextureComparisonFunc(m_compareOp);
+ desc.MaxLOD = m_mipmapMode == None ? 0.0f : 1000.0f;
+
+ QRHI_RES_RHI(QRhiD3D11);
+ HRESULT hr = rhiD->dev->CreateSamplerState(&desc, &samplerState);
+ if (FAILED(hr)) {
+ qWarning("Failed to create sampler state: %s", qPrintable(comErrorMessage(hr)));
+ return false;
+ }
+
+ generation += 1;
+ rhiD->registerResource(this);
+ return true;
+}
+
+// dummy, no Vulkan-style RenderPass+Framebuffer concept here
+QD3D11RenderPassDescriptor::QD3D11RenderPassDescriptor(QRhiImplementation *rhi)
+ : QRhiRenderPassDescriptor(rhi)
+{
+}
+
+QD3D11RenderPassDescriptor::~QD3D11RenderPassDescriptor()
+{
+ release();
+}
+
+void QD3D11RenderPassDescriptor::release()
+{
+ // nothing to do here
+}
+
+QD3D11ReferenceRenderTarget::QD3D11ReferenceRenderTarget(QRhiImplementation *rhi)
+ : QRhiRenderTarget(rhi),
+ d(rhi)
+{
+}
+
+QD3D11ReferenceRenderTarget::~QD3D11ReferenceRenderTarget()
+{
+ release();
+}
+
+void QD3D11ReferenceRenderTarget::release()
+{
+ // nothing to do here
+}
+
+QSize QD3D11ReferenceRenderTarget::pixelSize() const
+{
+ return d.pixelSize;
+}
+
+float QD3D11ReferenceRenderTarget::devicePixelRatio() const
+{
+ return d.dpr;
+}
+
+int QD3D11ReferenceRenderTarget::sampleCount() const
+{
+ return d.sampleCount;
+}
+
+QD3D11TextureRenderTarget::QD3D11TextureRenderTarget(QRhiImplementation *rhi,
+ const QRhiTextureRenderTargetDescription &desc,
+ Flags flags)
+ : QRhiTextureRenderTarget(rhi, desc, flags),
+ d(rhi)
+{
+ for (int i = 0; i < QD3D11RenderTargetData::MAX_COLOR_ATTACHMENTS; ++i) {
+ ownsRtv[i] = false;
+ rtv[i] = nullptr;
+ }
+}
+
+QD3D11TextureRenderTarget::~QD3D11TextureRenderTarget()
+{
+ release();
+}
+
+void QD3D11TextureRenderTarget::release()
+{
+ QRHI_RES_RHI(QRhiD3D11);
+
+ if (!rtv[0] && !dsv)
+ return;
+
+ if (dsv) {
+ if (ownsDsv)
+ dsv->Release();
+ dsv = nullptr;
+ }
+
+ for (int i = 0; i < QD3D11RenderTargetData::MAX_COLOR_ATTACHMENTS; ++i) {
+ if (rtv[i]) {
+ if (ownsRtv[i])
+ rtv[i]->Release();
+ rtv[i] = nullptr;
+ }
+ }
+
+ rhiD->unregisterResource(this);
+}
+
+QRhiRenderPassDescriptor *QD3D11TextureRenderTarget::newCompatibleRenderPassDescriptor()
+{
+ return new QD3D11RenderPassDescriptor(m_rhi);
+}
+
+bool QD3D11TextureRenderTarget::build()
+{
+ if (rtv[0] || dsv)
+ release();
+
+ const QVector<QRhiColorAttachment> colorAttachments = m_desc.colorAttachments();
+ Q_ASSERT(!colorAttachments.isEmpty() || m_desc.depthTexture());
+ Q_ASSERT(!m_desc.depthStencilBuffer() || !m_desc.depthTexture());
+ const bool hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
+
+ QRHI_RES_RHI(QRhiD3D11);
+
+ d.colorAttCount = colorAttachments.count();
+ for (int i = 0; i < d.colorAttCount; ++i) {
+ QRhiTexture *texture = colorAttachments[i].texture();
+ QRhiRenderBuffer *rb = colorAttachments[i].renderBuffer();
+ Q_ASSERT(texture || rb);
+ if (texture) {
+ QD3D11Texture *texD = QRHI_RES(QD3D11Texture, texture);
+ D3D11_RENDER_TARGET_VIEW_DESC rtvDesc;
+ memset(&rtvDesc, 0, sizeof(rtvDesc));
+ rtvDesc.Format = toD3DTextureFormat(texD->format(), texD->flags());
+ if (texD->flags().testFlag(QRhiTexture::CubeMap)) {
+ rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY;
+ rtvDesc.Texture2DArray.MipSlice = colorAttachments[i].level();
+ rtvDesc.Texture2DArray.FirstArraySlice = colorAttachments[i].layer();
+ rtvDesc.Texture2DArray.ArraySize = 1;
+ } else {
+ if (texD->sampleDesc.Count > 1) {
+ rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMS;
+ } else {
+ rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
+ rtvDesc.Texture2D.MipSlice = colorAttachments[i].level();
+ }
+ }
+ HRESULT hr = rhiD->dev->CreateRenderTargetView(texD->tex, &rtvDesc, &rtv[i]);
+ if (FAILED(hr)) {
+ qWarning("Failed to create rtv: %s", qPrintable(comErrorMessage(hr)));
+ return false;
+ }
+ ownsRtv[i] = true;
+ if (i == 0) {
+ d.pixelSize = texD->pixelSize();
+ d.sampleCount = texD->sampleDesc.Count;
+ }
+ } else if (rb) {
+ QD3D11RenderBuffer *rbD = QRHI_RES(QD3D11RenderBuffer, rb);
+ ownsRtv[i] = false;
+ rtv[i] = rbD->rtv;
+ if (i == 0) {
+ d.pixelSize = rbD->pixelSize();
+ d.sampleCount = rbD->sampleDesc.Count;
+ }
+ }
+ }
+ d.dpr = 1;
+
+ if (hasDepthStencil) {
+ if (m_desc.depthTexture()) {
+ ownsDsv = true;
+ QD3D11Texture *depthTexD = QRHI_RES(QD3D11Texture, m_desc.depthTexture());
+ D3D11_DEPTH_STENCIL_VIEW_DESC dsvDesc;
+ memset(&dsvDesc, 0, sizeof(dsvDesc));
+ dsvDesc.Format = toD3DDepthTextureDSVFormat(depthTexD->format());
+ dsvDesc.ViewDimension = depthTexD->sampleDesc.Count > 1 ? D3D11_DSV_DIMENSION_TEXTURE2DMS
+ : D3D11_DSV_DIMENSION_TEXTURE2D;
+ HRESULT hr = rhiD->dev->CreateDepthStencilView(depthTexD->tex, &dsvDesc, &dsv);
+ if (FAILED(hr)) {
+ qWarning("Failed to create dsv: %s", qPrintable(comErrorMessage(hr)));
+ return false;
+ }
+ if (d.colorAttCount == 0) {
+ d.pixelSize = depthTexD->pixelSize();
+ d.sampleCount = depthTexD->sampleDesc.Count;
+ }
+ } else {
+ ownsDsv = false;
+ QD3D11RenderBuffer *depthRbD = QRHI_RES(QD3D11RenderBuffer, m_desc.depthStencilBuffer());
+ dsv = depthRbD->dsv;
+ if (d.colorAttCount == 0) {
+ d.pixelSize = m_desc.depthStencilBuffer()->pixelSize();
+ d.sampleCount = depthRbD->sampleDesc.Count;
+ }
+ }
+ d.dsAttCount = 1;
+ } else {
+ d.dsAttCount = 0;
+ }
+
+ for (int i = 0; i < QD3D11RenderTargetData::MAX_COLOR_ATTACHMENTS; ++i)
+ d.rtv[i] = i < d.colorAttCount ? rtv[i] : nullptr;
+
+ d.dsv = dsv;
+ d.rp = QRHI_RES(QD3D11RenderPassDescriptor, m_renderPassDesc);
+
+ rhiD->registerResource(this);
+ return true;
+}
+
+QSize QD3D11TextureRenderTarget::pixelSize() const
+{
+ return d.pixelSize;
+}
+
+float QD3D11TextureRenderTarget::devicePixelRatio() const
+{
+ return d.dpr;
+}
+
+int QD3D11TextureRenderTarget::sampleCount() const
+{
+ return d.sampleCount;
+}
+
+QD3D11ShaderResourceBindings::QD3D11ShaderResourceBindings(QRhiImplementation *rhi)
+ : QRhiShaderResourceBindings(rhi)
+{
+}
+
+QD3D11ShaderResourceBindings::~QD3D11ShaderResourceBindings()
+{
+ release();
+}
+
+void QD3D11ShaderResourceBindings::release()
+{
+ sortedBindings.clear();
+}
+
+bool QD3D11ShaderResourceBindings::build()
+{
+ if (!sortedBindings.isEmpty())
+ release();
+
+ sortedBindings = m_bindings;
+ std::sort(sortedBindings.begin(), sortedBindings.end(),
+ [](const QRhiShaderResourceBinding &a, const QRhiShaderResourceBinding &b)
+ {
+ return QRhiShaderResourceBindingPrivate::get(&a)->binding < QRhiShaderResourceBindingPrivate::get(&b)->binding;
+ });
+
+ boundResourceData.resize(sortedBindings.count());
+
+ QRHI_RES_RHI(QRhiD3D11);
+ rhiD->updateShaderResourceBindings(this);
+
+ generation += 1;
+ return true;
+}
+
+QD3D11GraphicsPipeline::QD3D11GraphicsPipeline(QRhiImplementation *rhi)
+ : QRhiGraphicsPipeline(rhi)
+{
+}
+
+QD3D11GraphicsPipeline::~QD3D11GraphicsPipeline()
+{
+ release();
+}
+
+void QD3D11GraphicsPipeline::release()
+{
+ QRHI_RES_RHI(QRhiD3D11);
+
+ if (!dsState)
+ return;
+
+ dsState->Release();
+ dsState = nullptr;
+
+ if (blendState) {
+ blendState->Release();
+ blendState = nullptr;
+ }
+
+ if (inputLayout) {
+ inputLayout->Release();
+ inputLayout = nullptr;
+ }
+
+ if (rastState) {
+ rastState->Release();
+ rastState = nullptr;
+ }
+
+ if (vs) {
+ vs->Release();
+ vs = nullptr;
+ }
+
+ if (fs) {
+ fs->Release();
+ fs = nullptr;
+ }
+
+ rhiD->unregisterResource(this);
+}
+
+static inline D3D11_CULL_MODE toD3DCullMode(QRhiGraphicsPipeline::CullMode c)
+{
+ switch (c) {
+ case QRhiGraphicsPipeline::None:
+ return D3D11_CULL_NONE;
+ case QRhiGraphicsPipeline::Front:
+ return D3D11_CULL_FRONT;
+ case QRhiGraphicsPipeline::Back:
+ return D3D11_CULL_BACK;
+ default:
+ Q_UNREACHABLE();
+ return D3D11_CULL_NONE;
+ }
+}
+
+static inline D3D11_COMPARISON_FUNC toD3DCompareOp(QRhiGraphicsPipeline::CompareOp op)
+{
+ switch (op) {
+ case QRhiGraphicsPipeline::Never:
+ return D3D11_COMPARISON_NEVER;
+ case QRhiGraphicsPipeline::Less:
+ return D3D11_COMPARISON_LESS;
+ case QRhiGraphicsPipeline::Equal:
+ return D3D11_COMPARISON_EQUAL;
+ case QRhiGraphicsPipeline::LessOrEqual:
+ return D3D11_COMPARISON_LESS_EQUAL;
+ case QRhiGraphicsPipeline::Greater:
+ return D3D11_COMPARISON_GREATER;
+ case QRhiGraphicsPipeline::NotEqual:
+ return D3D11_COMPARISON_NOT_EQUAL;
+ case QRhiGraphicsPipeline::GreaterOrEqual:
+ return D3D11_COMPARISON_GREATER_EQUAL;
+ case QRhiGraphicsPipeline::Always:
+ return D3D11_COMPARISON_ALWAYS;
+ default:
+ Q_UNREACHABLE();
+ return D3D11_COMPARISON_ALWAYS;
+ }
+}
+
+static inline D3D11_STENCIL_OP toD3DStencilOp(QRhiGraphicsPipeline::StencilOp op)
+{
+ switch (op) {
+ case QRhiGraphicsPipeline::StencilZero:
+ return D3D11_STENCIL_OP_ZERO;
+ case QRhiGraphicsPipeline::Keep:
+ return D3D11_STENCIL_OP_KEEP;
+ case QRhiGraphicsPipeline::Replace:
+ return D3D11_STENCIL_OP_REPLACE;
+ case QRhiGraphicsPipeline::IncrementAndClamp:
+ return D3D11_STENCIL_OP_INCR_SAT;
+ case QRhiGraphicsPipeline::DecrementAndClamp:
+ return D3D11_STENCIL_OP_DECR_SAT;
+ case QRhiGraphicsPipeline::Invert:
+ return D3D11_STENCIL_OP_INVERT;
+ case QRhiGraphicsPipeline::IncrementAndWrap:
+ return D3D11_STENCIL_OP_INCR;
+ case QRhiGraphicsPipeline::DecrementAndWrap:
+ return D3D11_STENCIL_OP_DECR;
+ default:
+ Q_UNREACHABLE();
+ return D3D11_STENCIL_OP_KEEP;
+ }
+}
+
+static inline DXGI_FORMAT toD3DAttributeFormat(QRhiVertexInputAttribute::Format format)
+{
+ switch (format) {
+ case QRhiVertexInputAttribute::Float4:
+ return DXGI_FORMAT_R32G32B32A32_FLOAT;
+ case QRhiVertexInputAttribute::Float3:
+ return DXGI_FORMAT_R32G32B32_FLOAT;
+ case QRhiVertexInputAttribute::Float2:
+ return DXGI_FORMAT_R32G32_FLOAT;
+ case QRhiVertexInputAttribute::Float:
+ return DXGI_FORMAT_R32_FLOAT;
+ case QRhiVertexInputAttribute::UNormByte4:
+ return DXGI_FORMAT_R8G8B8A8_UNORM;
+ case QRhiVertexInputAttribute::UNormByte2:
+ return DXGI_FORMAT_R8G8_UNORM;
+ case QRhiVertexInputAttribute::UNormByte:
+ return DXGI_FORMAT_R8_UNORM;
+ default:
+ Q_UNREACHABLE();
+ return DXGI_FORMAT_R32G32B32A32_FLOAT;
+ }
+}
+
+static inline D3D11_PRIMITIVE_TOPOLOGY toD3DTopology(QRhiGraphicsPipeline::Topology t)
+{
+ switch (t) {
+ case QRhiGraphicsPipeline::Triangles:
+ return D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
+ case QRhiGraphicsPipeline::TriangleStrip:
+ return D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP;
+ case QRhiGraphicsPipeline::Lines:
+ return D3D11_PRIMITIVE_TOPOLOGY_LINELIST;
+ case QRhiGraphicsPipeline::LineStrip:
+ return D3D11_PRIMITIVE_TOPOLOGY_LINESTRIP;
+ case QRhiGraphicsPipeline::Points:
+ return D3D11_PRIMITIVE_TOPOLOGY_POINTLIST;
+ default:
+ Q_UNREACHABLE();
+ return D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
+ }
+}
+
+static inline uint toD3DColorWriteMask(QRhiGraphicsPipeline::ColorMask c)
+{
+ uint f = 0;
+ if (c.testFlag(QRhiGraphicsPipeline::R))
+ f |= D3D11_COLOR_WRITE_ENABLE_RED;
+ if (c.testFlag(QRhiGraphicsPipeline::G))
+ f |= D3D11_COLOR_WRITE_ENABLE_GREEN;
+ if (c.testFlag(QRhiGraphicsPipeline::B))
+ f |= D3D11_COLOR_WRITE_ENABLE_BLUE;
+ if (c.testFlag(QRhiGraphicsPipeline::A))
+ f |= D3D11_COLOR_WRITE_ENABLE_ALPHA;
+ return f;
+}
+
+static inline D3D11_BLEND toD3DBlendFactor(QRhiGraphicsPipeline::BlendFactor f)
+{
+ switch (f) {
+ case QRhiGraphicsPipeline::Zero:
+ return D3D11_BLEND_ZERO;
+ case QRhiGraphicsPipeline::One:
+ return D3D11_BLEND_ONE;
+ case QRhiGraphicsPipeline::SrcColor:
+ return D3D11_BLEND_SRC_COLOR;
+ case QRhiGraphicsPipeline::OneMinusSrcColor:
+ return D3D11_BLEND_INV_SRC_COLOR;
+ case QRhiGraphicsPipeline::DstColor:
+ return D3D11_BLEND_DEST_COLOR;
+ case QRhiGraphicsPipeline::OneMinusDstColor:
+ return D3D11_BLEND_INV_DEST_COLOR;
+ case QRhiGraphicsPipeline::SrcAlpha:
+ return D3D11_BLEND_SRC_ALPHA;
+ case QRhiGraphicsPipeline::OneMinusSrcAlpha:
+ return D3D11_BLEND_INV_SRC_ALPHA;
+ case QRhiGraphicsPipeline::DstAlpha:
+ return D3D11_BLEND_DEST_ALPHA;
+ case QRhiGraphicsPipeline::OneMinusDstAlpha:
+ return D3D11_BLEND_INV_DEST_ALPHA;
+ case QRhiGraphicsPipeline::ConstantColor:
+ Q_FALLTHROUGH();
+ case QRhiGraphicsPipeline::ConstantAlpha:
+ return D3D11_BLEND_BLEND_FACTOR;
+ case QRhiGraphicsPipeline::OneMinusConstantColor:
+ Q_FALLTHROUGH();
+ case QRhiGraphicsPipeline::OneMinusConstantAlpha:
+ return D3D11_BLEND_INV_BLEND_FACTOR;
+ case QRhiGraphicsPipeline::SrcAlphaSaturate:
+ return D3D11_BLEND_SRC_ALPHA_SAT;
+ case QRhiGraphicsPipeline::Src1Color:
+ return D3D11_BLEND_SRC1_COLOR;
+ case QRhiGraphicsPipeline::OneMinusSrc1Color:
+ return D3D11_BLEND_INV_SRC1_COLOR;
+ case QRhiGraphicsPipeline::Src1Alpha:
+ return D3D11_BLEND_SRC1_ALPHA;
+ case QRhiGraphicsPipeline::OneMinusSrc1Alpha:
+ return D3D11_BLEND_INV_SRC1_ALPHA;
+ default:
+ Q_UNREACHABLE();
+ return D3D11_BLEND_ZERO;
+ }
+}
+
+static inline D3D11_BLEND_OP toD3DBlendOp(QRhiGraphicsPipeline::BlendOp op)
+{
+ switch (op) {
+ case QRhiGraphicsPipeline::Add:
+ return D3D11_BLEND_OP_ADD;
+ case QRhiGraphicsPipeline::Subtract:
+ return D3D11_BLEND_OP_SUBTRACT;
+ case QRhiGraphicsPipeline::ReverseSubtract:
+ return D3D11_BLEND_OP_REV_SUBTRACT;
+ case QRhiGraphicsPipeline::Min:
+ return D3D11_BLEND_OP_MIN;
+ case QRhiGraphicsPipeline::Max:
+ return D3D11_BLEND_OP_MAX;
+ default:
+ Q_UNREACHABLE();
+ return D3D11_BLEND_OP_ADD;
+ }
+}
+
+static QByteArray compileHlslShaderSource(const QShader &shader, QShader::Variant shaderVariant, QString *error)
+{
+ QShaderCode dxbc = shader.shader({ QShader::DxbcShader, 50, shaderVariant });
+ if (!dxbc.shader().isEmpty())
+ return dxbc.shader();
+
+ QShaderCode hlslSource = shader.shader({ QShader::HlslShader, 50, shaderVariant });
+ if (hlslSource.shader().isEmpty()) {
+ qWarning() << "No HLSL (shader model 5.0) code found in baked shader" << shader;
+ return QByteArray();
+ }
+
+ const char *target;
+ switch (shader.stage()) {
+ case QShader::VertexStage:
+ target = "vs_5_0";
+ break;
+ case QShader::TessellationControlStage:
+ target = "hs_5_0";
+ break;
+ case QShader::TessellationEvaluationStage:
+ target = "ds_5_0";
+ break;
+ case QShader::GeometryStage:
+ target = "gs_5_0";
+ break;
+ case QShader::FragmentStage:
+ target = "ps_5_0";
+ break;
+ case QShader::ComputeStage:
+ target = "cs_5_0";
+ break;
+ default:
+ Q_UNREACHABLE();
+ return QByteArray();
+ }
+
+ ID3DBlob *bytecode = nullptr;
+ ID3DBlob *errors = nullptr;
+ HRESULT hr = D3DCompile(hlslSource.shader().constData(), hlslSource.shader().size(),
+ nullptr, nullptr, nullptr,
+ hlslSource.entryPoint().constData(), target, 0, 0, &bytecode, &errors);
+ if (FAILED(hr) || !bytecode) {
+ qWarning("HLSL shader compilation failed: 0x%x", uint(hr));
+ if (errors) {
+ *error = QString::fromUtf8(static_cast<const char *>(errors->GetBufferPointer()),
+ errors->GetBufferSize());
+ errors->Release();
+ }
+ return QByteArray();
+ }
+
+ QByteArray result;
+ result.resize(bytecode->GetBufferSize());
+ memcpy(result.data(), bytecode->GetBufferPointer(), result.size());
+ bytecode->Release();
+ return result;
+}
+
+bool QD3D11GraphicsPipeline::build()
+{
+ if (dsState)
+ release();
+
+ QRHI_RES_RHI(QRhiD3D11);
+
+ D3D11_RASTERIZER_DESC rastDesc;
+ memset(&rastDesc, 0, sizeof(rastDesc));
+ rastDesc.FillMode = D3D11_FILL_SOLID;
+ rastDesc.CullMode = toD3DCullMode(m_cullMode);
+ rastDesc.FrontCounterClockwise = m_frontFace == CCW;
+ rastDesc.ScissorEnable = m_flags.testFlag(UsesScissor);
+ rastDesc.MultisampleEnable = rhiD->effectiveSampleCount(m_sampleCount).Count > 1;
+ HRESULT hr = rhiD->dev->CreateRasterizerState(&rastDesc, &rastState);
+ if (FAILED(hr)) {
+ qWarning("Failed to create rasterizer state: %s", qPrintable(comErrorMessage(hr)));
+ return false;
+ }
+
+ D3D11_DEPTH_STENCIL_DESC dsDesc;
+ memset(&dsDesc, 0, sizeof(dsDesc));
+ dsDesc.DepthEnable = m_depthTest;
+ dsDesc.DepthWriteMask = m_depthWrite ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO;
+ dsDesc.DepthFunc = toD3DCompareOp(m_depthOp);
+ dsDesc.StencilEnable = m_stencilTest;
+ if (m_stencilTest) {
+ dsDesc.StencilReadMask = m_stencilReadMask;
+ dsDesc.StencilWriteMask = m_stencilWriteMask;
+ dsDesc.FrontFace.StencilFailOp = toD3DStencilOp(m_stencilFront.failOp);
+ dsDesc.FrontFace.StencilDepthFailOp = toD3DStencilOp(m_stencilFront.depthFailOp);
+ dsDesc.FrontFace.StencilPassOp = toD3DStencilOp(m_stencilFront.passOp);
+ dsDesc.FrontFace.StencilFunc = toD3DCompareOp(m_stencilFront.compareOp);
+ dsDesc.BackFace.StencilFailOp = toD3DStencilOp(m_stencilBack.failOp);
+ dsDesc.BackFace.StencilDepthFailOp = toD3DStencilOp(m_stencilBack.depthFailOp);
+ dsDesc.BackFace.StencilPassOp = toD3DStencilOp(m_stencilBack.passOp);
+ dsDesc.BackFace.StencilFunc = toD3DCompareOp(m_stencilBack.compareOp);
+ }
+ hr = rhiD->dev->CreateDepthStencilState(&dsDesc, &dsState);
+ if (FAILED(hr)) {
+ qWarning("Failed to create depth-stencil state: %s", qPrintable(comErrorMessage(hr)));
+ return false;
+ }
+
+ D3D11_BLEND_DESC blendDesc;
+ memset(&blendDesc, 0, sizeof(blendDesc));
+ blendDesc.IndependentBlendEnable = m_targetBlends.count() > 1;
+ for (int i = 0, ie = m_targetBlends.count(); i != ie; ++i) {
+ const QRhiGraphicsPipeline::TargetBlend &b(m_targetBlends[i]);
+ D3D11_RENDER_TARGET_BLEND_DESC blend;
+ memset(&blend, 0, sizeof(blend));
+ blend.BlendEnable = b.enable;
+ blend.SrcBlend = toD3DBlendFactor(b.srcColor);
+ blend.DestBlend = toD3DBlendFactor(b.dstColor);
+ blend.BlendOp = toD3DBlendOp(b.opColor);
+ blend.SrcBlendAlpha = toD3DBlendFactor(b.srcAlpha);
+ blend.DestBlendAlpha = toD3DBlendFactor(b.dstAlpha);
+ blend.BlendOpAlpha = toD3DBlendOp(b.opAlpha);
+ blend.RenderTargetWriteMask = toD3DColorWriteMask(b.colorWrite);
+ blendDesc.RenderTarget[i] = blend;
+ }
+ if (m_targetBlends.isEmpty()) {
+ D3D11_RENDER_TARGET_BLEND_DESC blend;
+ memset(&blend, 0, sizeof(blend));
+ blend.RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
+ blendDesc.RenderTarget[0] = blend;
+ }
+ hr = rhiD->dev->CreateBlendState(&blendDesc, &blendState);
+ if (FAILED(hr)) {
+ qWarning("Failed to create blend state: %s", qPrintable(comErrorMessage(hr)));
+ return false;
+ }
+
+ QByteArray vsByteCode;
+ for (const QRhiGraphicsShaderStage &shaderStage : qAsConst(m_shaderStages)) {
+ QString error;
+ QByteArray bytecode = compileHlslShaderSource(shaderStage.shader(), shaderStage.shaderVariant(), &error);
+ if (bytecode.isEmpty()) {
+ qWarning("HLSL shader compilation failed: %s", qPrintable(error));
+ return false;
+ }
+ switch (shaderStage.type()) {
+ case QRhiGraphicsShaderStage::Vertex:
+ hr = rhiD->dev->CreateVertexShader(bytecode.constData(), bytecode.size(), nullptr, &vs);
+ if (FAILED(hr)) {
+ qWarning("Failed to create vertex shader: %s", qPrintable(comErrorMessage(hr)));
+ return false;
+ }
+ vsByteCode = bytecode;
+ break;
+ case QRhiGraphicsShaderStage::Fragment:
+ hr = rhiD->dev->CreatePixelShader(bytecode.constData(), bytecode.size(), nullptr, &fs);
+ if (FAILED(hr)) {
+ qWarning("Failed to create pixel shader: %s", qPrintable(comErrorMessage(hr)));
+ return false;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ d3dTopology = toD3DTopology(m_topology);
+
+ if (!vsByteCode.isEmpty()) {
+ const QVector<QRhiVertexInputBinding> bindings = m_vertexInputLayout.bindings();
+ const QVector<QRhiVertexInputAttribute> attributes = m_vertexInputLayout.attributes();
+ QVarLengthArray<D3D11_INPUT_ELEMENT_DESC, 4> inputDescs;
+ for (const QRhiVertexInputAttribute &attribute : attributes) {
+ D3D11_INPUT_ELEMENT_DESC desc;
+ memset(&desc, 0, sizeof(desc));
+ // the output from SPIRV-Cross uses TEXCOORD<location> as the semantic
+ desc.SemanticName = "TEXCOORD";
+ desc.SemanticIndex = attribute.location();
+ desc.Format = toD3DAttributeFormat(attribute.format());
+ desc.InputSlot = attribute.binding();
+ desc.AlignedByteOffset = attribute.offset();
+ const QRhiVertexInputBinding &binding(bindings[attribute.binding()]);
+ if (binding.classification() == QRhiVertexInputBinding::PerInstance) {
+ desc.InputSlotClass = D3D11_INPUT_PER_INSTANCE_DATA;
+ desc.InstanceDataStepRate = binding.instanceStepRate();
+ } else {
+ desc.InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
+ }
+ inputDescs.append(desc);
+ }
+ hr = rhiD->dev->CreateInputLayout(inputDescs.constData(), inputDescs.count(), vsByteCode, vsByteCode.size(), &inputLayout);
+ if (FAILED(hr)) {
+ qWarning("Failed to create input layout: %s", qPrintable(comErrorMessage(hr)));
+ return false;
+ }
+ }
+
+ generation += 1;
+ rhiD->registerResource(this);
+ return true;
+}
+
+QD3D11CommandBuffer::QD3D11CommandBuffer(QRhiImplementation *rhi)
+ : QRhiCommandBuffer(rhi)
+{
+ resetState();
+}
+
+QD3D11CommandBuffer::~QD3D11CommandBuffer()
+{
+ release();
+}
+
+void QD3D11CommandBuffer::release()
+{
+ // nothing to do here
+}
+
+QD3D11SwapChain::QD3D11SwapChain(QRhiImplementation *rhi)
+ : QRhiSwapChain(rhi),
+ rt(rhi),
+ cb(rhi)
+{
+ for (int i = 0; i < BUFFER_COUNT; ++i) {
+ tex[i] = nullptr;
+ rtv[i] = nullptr;
+ msaaTex[i] = nullptr;
+ msaaRtv[i] = nullptr;
+ timestampActive[i] = false;
+ timestampDisjointQuery[i] = nullptr;
+ timestampQuery[2 * i] = nullptr;
+ timestampQuery[2 * i + 1] = nullptr;
+ }
+}
+
+QD3D11SwapChain::~QD3D11SwapChain()
+{
+ release();
+}
+
+void QD3D11SwapChain::releaseBuffers()
+{
+ for (int i = 0; i < BUFFER_COUNT; ++i) {
+ if (rtv[i]) {
+ rtv[i]->Release();
+ rtv[i] = nullptr;
+ }
+ if (tex[i]) {
+ tex[i]->Release();
+ tex[i] = nullptr;
+ }
+ if (msaaRtv[i]) {
+ msaaRtv[i]->Release();
+ msaaRtv[i] = nullptr;
+ }
+ if (msaaTex[i]) {
+ msaaTex[i]->Release();
+ msaaTex[i] = nullptr;
+ }
+ }
+}
+
+void QD3D11SwapChain::release()
+{
+ if (!swapChain)
+ return;
+
+ releaseBuffers();
+
+ for (int i = 0; i < BUFFER_COUNT; ++i) {
+ if (timestampDisjointQuery[i]) {
+ timestampDisjointQuery[i]->Release();
+ timestampDisjointQuery[i] = nullptr;
+ }
+ for (int j = 0; j < 2; ++j) {
+ const int idx = BUFFER_COUNT * i + j;
+ if (timestampQuery[idx]) {
+ timestampQuery[idx]->Release();
+ timestampQuery[idx] = nullptr;
+ }
+ }
+ }
+
+ swapChain->Release();
+ swapChain = nullptr;
+
+ QRHI_PROF;
+ QRHI_PROF_F(releaseSwapChain(this));
+
+ QRHI_RES_RHI(QRhiD3D11);
+ rhiD->unregisterResource(this);
+}
+
+QRhiCommandBuffer *QD3D11SwapChain::currentFrameCommandBuffer()
+{
+ return &cb;
+}
+
+QRhiRenderTarget *QD3D11SwapChain::currentFrameRenderTarget()
+{
+ return &rt;
+}
+
+QSize QD3D11SwapChain::surfacePixelSize()
+{
+ Q_ASSERT(m_window);
+ return m_window->size() * m_window->devicePixelRatio();
+}
+
+QRhiRenderPassDescriptor *QD3D11SwapChain::newCompatibleRenderPassDescriptor()
+{
+ return new QD3D11RenderPassDescriptor(m_rhi);
+}
+
+bool QD3D11SwapChain::newColorBuffer(const QSize &size, DXGI_FORMAT format, DXGI_SAMPLE_DESC sampleDesc,
+ ID3D11Texture2D **tex, ID3D11RenderTargetView **rtv) const
+{
+ D3D11_TEXTURE2D_DESC desc;
+ memset(&desc, 0, sizeof(desc));
+ desc.Width = size.width();
+ desc.Height = size.height();
+ desc.MipLevels = 1;
+ desc.ArraySize = 1;
+ desc.Format = format;
+ desc.SampleDesc = sampleDesc;
+ desc.Usage = D3D11_USAGE_DEFAULT;
+ desc.BindFlags = D3D11_BIND_RENDER_TARGET;
+
+ QRHI_RES_RHI(QRhiD3D11);
+ HRESULT hr = rhiD->dev->CreateTexture2D(&desc, nullptr, tex);
+ if (FAILED(hr)) {
+ qWarning("Failed to create color buffer texture: %s", qPrintable(comErrorMessage(hr)));
+ return false;
+ }
+
+ D3D11_RENDER_TARGET_VIEW_DESC rtvDesc;
+ memset(&rtvDesc, 0, sizeof(rtvDesc));
+ rtvDesc.Format = format;
+ rtvDesc.ViewDimension = sampleDesc.Count > 1 ? D3D11_RTV_DIMENSION_TEXTURE2DMS : D3D11_RTV_DIMENSION_TEXTURE2D;
+ hr = rhiD->dev->CreateRenderTargetView(*tex, &rtvDesc, rtv);
+ if (FAILED(hr)) {
+ qWarning("Failed to create color buffer rtv: %s", qPrintable(comErrorMessage(hr)));
+ (*tex)->Release();
+ *tex = nullptr;
+ return false;
+ }
+
+ return true;
+}
+
+bool QD3D11SwapChain::buildOrResize()
+{
+ // Can be called multiple times due to window resizes - that is not the
+ // same as a simple release+build (as with other resources). Just need to
+ // resize the buffers then.
+
+ const bool needsRegistration = !window || window != m_window;
+
+ // except if the window actually changes
+ if (window && window != m_window)
+ release();
+
+ window = m_window;
+ m_currentPixelSize = surfacePixelSize();
+ pixelSize = m_currentPixelSize;
+
+ if (pixelSize.isEmpty())
+ return false;
+
+ colorFormat = DXGI_FORMAT_R8G8B8A8_UNORM;
+ const DXGI_FORMAT srgbAdjustedFormat = m_flags.testFlag(sRGB) ?
+ DXGI_FORMAT_R8G8B8A8_UNORM_SRGB : DXGI_FORMAT_R8G8B8A8_UNORM;
+
+ const UINT swapChainFlags = 0;
+
+ QRHI_RES_RHI(QRhiD3D11);
+ if (!swapChain) {
+ HWND hwnd = reinterpret_cast<HWND>(window->winId());
+ sampleDesc = rhiD->effectiveSampleCount(m_sampleCount);
+
+ // We use FLIP_DISCARD which implies a buffer count of 2 (as opposed to the
+ // old DISCARD with back buffer count == 1). This makes no difference for
+ // the rest of the stuff except that automatic MSAA is unsupported and
+ // needs to be implemented via a custom multisample render target and an
+ // explicit resolve.
+
+ HRESULT hr;
+ if (rhiD->hasDxgi2) {
+ DXGI_SWAP_CHAIN_DESC1 desc;
+ memset(&desc, 0, sizeof(desc));
+ desc.Width = pixelSize.width();
+ desc.Height = pixelSize.height();
+ desc.Format = colorFormat;
+ desc.SampleDesc.Count = 1;
+ desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+ desc.BufferCount = BUFFER_COUNT;
+ desc.Scaling = DXGI_SCALING_STRETCH;
+ desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
+ if (m_flags.testFlag(SurfaceHasPreMulAlpha))
+ desc.AlphaMode = DXGI_ALPHA_MODE_PREMULTIPLIED;
+ else if (m_flags.testFlag(SurfaceHasNonPreMulAlpha))
+ desc.AlphaMode = DXGI_ALPHA_MODE_STRAIGHT;
+ desc.Flags = swapChainFlags;
+
+ IDXGISwapChain1 *sc1;
+ hr = static_cast<IDXGIFactory2 *>(rhiD->dxgiFactory)->CreateSwapChainForHwnd(rhiD->dev, hwnd, &desc,
+ nullptr, nullptr, &sc1);
+ if (SUCCEEDED(hr))
+ swapChain = sc1;
+ } else {
+ // Windows 7
+ DXGI_SWAP_CHAIN_DESC desc;
+ memset(&desc, 0, sizeof(desc));
+ desc.BufferDesc.Width = pixelSize.width();
+ desc.BufferDesc.Height = pixelSize.height();
+ desc.BufferDesc.RefreshRate.Numerator = 60;
+ desc.BufferDesc.RefreshRate.Denominator = 1;
+ desc.BufferDesc.Format = colorFormat;
+ desc.SampleDesc.Count = 1;
+ desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+ desc.BufferCount = BUFFER_COUNT;
+ desc.OutputWindow = hwnd;
+ desc.Windowed = true;
+ desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
+ desc.Flags = swapChainFlags;
+
+ hr = rhiD->dxgiFactory->CreateSwapChain(rhiD->dev, &desc, &swapChain);
+ }
+ if (FAILED(hr)) {
+ qWarning("Failed to create D3D11 swapchain: %s", qPrintable(comErrorMessage(hr)));
+ return false;
+ }
+ } else {
+ releaseBuffers();
+ HRESULT hr = swapChain->ResizeBuffers(2, pixelSize.width(), pixelSize.height(), colorFormat, swapChainFlags);
+ if (FAILED(hr)) {
+ qWarning("Failed to resize D3D11 swapchain: %s", qPrintable(comErrorMessage(hr)));
+ return false;
+ }
+ }
+
+ for (int i = 0; i < BUFFER_COUNT; ++i) {
+ HRESULT hr = swapChain->GetBuffer(0, IID_ID3D11Texture2D, reinterpret_cast<void **>(&tex[i]));
+ if (FAILED(hr)) {
+ qWarning("Failed to query swapchain buffer %d: %s", i, qPrintable(comErrorMessage(hr)));
+ return false;
+ }
+ D3D11_RENDER_TARGET_VIEW_DESC rtvDesc;
+ memset(&rtvDesc, 0, sizeof(rtvDesc));
+ rtvDesc.Format = srgbAdjustedFormat;
+ rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
+ hr = rhiD->dev->CreateRenderTargetView(tex[i], &rtvDesc, &rtv[i]);
+ if (FAILED(hr)) {
+ qWarning("Failed to create rtv for swapchain buffer %d: %s", i, qPrintable(comErrorMessage(hr)));
+ return false;
+ }
+ if (sampleDesc.Count > 1) {
+ if (!newColorBuffer(pixelSize, srgbAdjustedFormat, sampleDesc, &msaaTex[i], &msaaRtv[i]))
+ return false;
+ }
+ }
+
+ if (m_depthStencil && m_depthStencil->sampleCount() != m_sampleCount) {
+ qWarning("Depth-stencil buffer's sampleCount (%d) does not match color buffers' sample count (%d). Expect problems.",
+ m_depthStencil->sampleCount(), m_sampleCount);
+ }
+ if (m_depthStencil && m_depthStencil->pixelSize() != pixelSize) {
+ qWarning("Depth-stencil buffer's size (%dx%d) does not match the surface size (%dx%d). Expect problems.",
+ m_depthStencil->pixelSize().width(), m_depthStencil->pixelSize().height(),
+ pixelSize.width(), pixelSize.height());
+ }
+
+ currentFrameSlot = 0;
+ frameCount = 0;
+ ds = m_depthStencil ? QRHI_RES(QD3D11RenderBuffer, m_depthStencil) : nullptr;
+ swapInterval = m_flags.testFlag(QRhiSwapChain::NoVSync) ? 0 : 1;
+
+ QD3D11ReferenceRenderTarget *rtD = QRHI_RES(QD3D11ReferenceRenderTarget, &rt);
+ rtD->d.rp = QRHI_RES(QD3D11RenderPassDescriptor, m_renderPassDesc);
+ rtD->d.pixelSize = pixelSize;
+ rtD->d.dpr = window->devicePixelRatio();
+ rtD->d.sampleCount = sampleDesc.Count;
+ rtD->d.colorAttCount = 1;
+ rtD->d.dsAttCount = m_depthStencil ? 1 : 0;
+
+ QRHI_PROF;
+ QRHI_PROF_F(resizeSwapChain(this, BUFFER_COUNT, sampleDesc.Count > 1 ? BUFFER_COUNT : 0, sampleDesc.Count));
+ if (rhiP) {
+ D3D11_QUERY_DESC queryDesc;
+ memset(&queryDesc, 0, sizeof(queryDesc));
+ for (int i = 0; i < BUFFER_COUNT; ++i) {
+ if (!timestampDisjointQuery[i]) {
+ queryDesc.Query = D3D11_QUERY_TIMESTAMP_DISJOINT;
+ HRESULT hr = rhiD->dev->CreateQuery(&queryDesc, &timestampDisjointQuery[i]);
+ if (FAILED(hr)) {
+ qWarning("Failed to create timestamp disjoint query: %s", qPrintable(comErrorMessage(hr)));
+ break;
+ }
+ }
+ queryDesc.Query = D3D11_QUERY_TIMESTAMP;
+ for (int j = 0; j < 2; ++j) {
+ const int idx = BUFFER_COUNT * i + j; // one pair per buffer (frame)
+ if (!timestampQuery[idx]) {
+ HRESULT hr = rhiD->dev->CreateQuery(&queryDesc, &timestampQuery[idx]);
+ if (FAILED(hr)) {
+ qWarning("Failed to create timestamp query: %s", qPrintable(comErrorMessage(hr)));
+ break;
+ }
+ }
+ }
+ }
+ // timestamp queries are optional so we can go on even if they failed
+ }
+
+ if (needsRegistration)
+ rhiD->registerResource(this);
+
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/rhi/qrhid3d11_p.h b/src/gui/rhi/qrhid3d11_p.h
new file mode 100644
index 0000000000..3e2e492d9c
--- /dev/null
+++ b/src/gui/rhi/qrhid3d11_p.h
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gui module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QRHID3D11_H
+#define QRHID3D11_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qrhi_p.h>
+
+// no d3d includes here, to prevent precompiled header mess (due to this being
+// a public header)
+
+QT_BEGIN_NAMESPACE
+
+struct Q_GUI_EXPORT QRhiD3D11InitParams : public QRhiInitParams
+{
+ bool enableDebugLayer = false;
+};
+
+struct Q_GUI_EXPORT QRhiD3D11NativeHandles : public QRhiNativeHandles
+{
+ void *dev = nullptr;
+ void *context = nullptr;
+};
+
+struct Q_GUI_EXPORT QRhiD3D11TextureNativeHandles : public QRhiNativeHandles
+{
+ void *texture = nullptr; // ID3D11Texture2D*
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/rhi/qrhid3d11_p_p.h b/src/gui/rhi/qrhid3d11_p_p.h
new file mode 100644
index 0000000000..fd5247b0b4
--- /dev/null
+++ b/src/gui/rhi/qrhid3d11_p_p.h
@@ -0,0 +1,624 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gui module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QRHID3D11_P_H
+#define QRHID3D11_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qrhid3d11_p.h"
+#include "qrhi_p_p.h"
+#include "qshaderdescription_p.h"
+#include <QWindow>
+
+#include <d3d11_1.h>
+#include <dxgi1_3.h>
+
+QT_BEGIN_NAMESPACE
+
+struct QD3D11Buffer : public QRhiBuffer
+{
+ QD3D11Buffer(QRhiImplementation *rhi, Type type, UsageFlags usage, int size);
+ ~QD3D11Buffer();
+ void release() override;
+ bool build() override;
+
+ ID3D11Buffer *buffer = nullptr;
+ QByteArray dynBuf;
+ bool hasPendingDynamicUpdates = false;
+ uint generation = 0;
+ friend class QRhiD3D11;
+};
+
+struct QD3D11RenderBuffer : public QRhiRenderBuffer
+{
+ QD3D11RenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize,
+ int sampleCount, QRhiRenderBuffer::Flags flags);
+ ~QD3D11RenderBuffer();
+ void release() override;
+ bool build() override;
+ QRhiTexture::Format backingFormat() const override;
+
+ ID3D11Texture2D *tex = nullptr;
+ ID3D11DepthStencilView *dsv = nullptr;
+ ID3D11RenderTargetView *rtv = nullptr;
+ DXGI_FORMAT dxgiFormat;
+ DXGI_SAMPLE_DESC sampleDesc;
+ friend class QRhiD3D11;
+};
+
+struct QD3D11Texture : public QRhiTexture
+{
+ QD3D11Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize,
+ int sampleCount, Flags flags);
+ ~QD3D11Texture();
+ void release() override;
+ bool build() override;
+ bool buildFrom(const QRhiNativeHandles *src) override;
+ const QRhiNativeHandles *nativeHandles() override;
+
+ bool prepareBuild(QSize *adjustedSize = nullptr);
+ bool finishBuild();
+
+ ID3D11Texture2D *tex = nullptr;
+ bool owns = true;
+ ID3D11ShaderResourceView *srv = nullptr;
+ DXGI_FORMAT dxgiFormat;
+ uint mipLevelCount = 0;
+ DXGI_SAMPLE_DESC sampleDesc;
+ QRhiD3D11TextureNativeHandles nativeHandlesStruct;
+ uint generation = 0;
+ friend class QRhiD3D11;
+};
+
+struct QD3D11Sampler : public QRhiSampler
+{
+ QD3D11Sampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode,
+ AddressMode u, AddressMode v);
+ ~QD3D11Sampler();
+ void release() override;
+ bool build() override;
+
+ ID3D11SamplerState *samplerState = nullptr;
+ uint generation = 0;
+ friend class QRhiD3D11;
+};
+
+struct QD3D11RenderPassDescriptor : public QRhiRenderPassDescriptor
+{
+ QD3D11RenderPassDescriptor(QRhiImplementation *rhi);
+ ~QD3D11RenderPassDescriptor();
+ void release() override;
+};
+
+struct QD3D11RenderTargetData
+{
+ QD3D11RenderTargetData(QRhiImplementation *)
+ {
+ for (int i = 0; i < MAX_COLOR_ATTACHMENTS; ++i)
+ rtv[i] = nullptr;
+ }
+
+ QD3D11RenderPassDescriptor *rp = nullptr;
+ QSize pixelSize;
+ float dpr = 1;
+ int sampleCount = 1;
+ int colorAttCount = 0;
+ int dsAttCount = 0;
+
+ static const int MAX_COLOR_ATTACHMENTS = 8;
+ ID3D11RenderTargetView *rtv[MAX_COLOR_ATTACHMENTS];
+ ID3D11DepthStencilView *dsv = nullptr;
+};
+
+struct QD3D11ReferenceRenderTarget : public QRhiRenderTarget
+{
+ QD3D11ReferenceRenderTarget(QRhiImplementation *rhi);
+ ~QD3D11ReferenceRenderTarget();
+ void release() override;
+
+ QSize pixelSize() const override;
+ float devicePixelRatio() const override;
+ int sampleCount() const override;
+
+ QD3D11RenderTargetData d;
+};
+
+struct QD3D11TextureRenderTarget : public QRhiTextureRenderTarget
+{
+ QD3D11TextureRenderTarget(QRhiImplementation *rhi, const QRhiTextureRenderTargetDescription &desc, Flags flags);
+ ~QD3D11TextureRenderTarget();
+ void release() override;
+
+ QSize pixelSize() const override;
+ float devicePixelRatio() const override;
+ int sampleCount() const override;
+
+ QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override;
+ bool build() override;
+
+ QD3D11RenderTargetData d;
+ bool ownsRtv[QD3D11RenderTargetData::MAX_COLOR_ATTACHMENTS];
+ ID3D11RenderTargetView *rtv[QD3D11RenderTargetData::MAX_COLOR_ATTACHMENTS];
+ bool ownsDsv = false;
+ ID3D11DepthStencilView *dsv = nullptr;
+ friend class QRhiD3D11;
+};
+
+struct QD3D11ShaderResourceBindings : public QRhiShaderResourceBindings
+{
+ QD3D11ShaderResourceBindings(QRhiImplementation *rhi);
+ ~QD3D11ShaderResourceBindings();
+ void release() override;
+ bool build() override;
+
+ QVector<QRhiShaderResourceBinding> sortedBindings;
+ uint generation = 0;
+
+ // Keep track of the generation number of each referenced QRhi* to be able
+ // to detect that the batched bindings are out of date.
+ struct BoundUniformBufferData {
+ quint64 id;
+ uint generation;
+ };
+ struct BoundSampledTextureData {
+ quint64 texId;
+ uint texGeneration;
+ quint64 samplerId;
+ uint samplerGeneration;
+ };
+ struct BoundResourceData {
+ union {
+ BoundUniformBufferData ubuf;
+ BoundSampledTextureData stex;
+ };
+ };
+ QVector<BoundResourceData> boundResourceData;
+
+ QRhiBatchedBindings<ID3D11Buffer *> vsubufs;
+ QRhiBatchedBindings<UINT> vsubufoffsets;
+ QRhiBatchedBindings<UINT> vsubufsizes;
+
+ QRhiBatchedBindings<ID3D11Buffer *> fsubufs;
+ QRhiBatchedBindings<UINT> fsubufoffsets;
+ QRhiBatchedBindings<UINT> fsubufsizes;
+
+ QRhiBatchedBindings<ID3D11SamplerState *> vssamplers;
+ QRhiBatchedBindings<ID3D11ShaderResourceView *> vsshaderresources;
+
+ QRhiBatchedBindings<ID3D11SamplerState *> fssamplers;
+ QRhiBatchedBindings<ID3D11ShaderResourceView *> fsshaderresources;
+
+ friend class QRhiD3D11;
+};
+
+Q_DECLARE_TYPEINFO(QD3D11ShaderResourceBindings::BoundResourceData, Q_MOVABLE_TYPE);
+
+struct QD3D11GraphicsPipeline : public QRhiGraphicsPipeline
+{
+ QD3D11GraphicsPipeline(QRhiImplementation *rhi);
+ ~QD3D11GraphicsPipeline();
+ void release() override;
+ bool build() override;
+
+ ID3D11DepthStencilState *dsState = nullptr;
+ ID3D11BlendState *blendState = nullptr;
+ ID3D11VertexShader *vs = nullptr;
+ ID3D11PixelShader *fs = nullptr;
+ ID3D11InputLayout *inputLayout = nullptr;
+ D3D11_PRIMITIVE_TOPOLOGY d3dTopology = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
+ ID3D11RasterizerState *rastState = nullptr;
+ uint generation = 0;
+ friend class QRhiD3D11;
+};
+
+struct QD3D11SwapChain;
+
+struct QD3D11CommandBuffer : public QRhiCommandBuffer
+{
+ QD3D11CommandBuffer(QRhiImplementation *rhi);
+ ~QD3D11CommandBuffer();
+ void release() override;
+
+ struct Command {
+ enum Cmd {
+ SetRenderTarget,
+ Clear,
+ Viewport,
+ Scissor,
+ BindVertexBuffers,
+ BindIndexBuffer,
+ BindGraphicsPipeline,
+ BindShaderResources,
+ StencilRef,
+ BlendConstants,
+ Draw,
+ DrawIndexed,
+ UpdateSubRes,
+ CopySubRes,
+ ResolveSubRes,
+ GenMip,
+ DebugMarkBegin,
+ DebugMarkEnd,
+ DebugMarkMsg
+ };
+ enum ClearFlag { Color = 1, Depth = 2, Stencil = 4 };
+ Cmd cmd;
+
+ static const int MAX_UBUF_BINDINGS = 32; // should be D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT but 128 is a waste of space for our purposes
+
+ // QRhi*/QD3D11* references should be kept at minimum (so no
+ // QRhiTexture/Buffer/etc. pointers).
+ union {
+ struct {
+ QRhiRenderTarget *rt;
+ } setRenderTarget;
+ struct {
+ QRhiRenderTarget *rt;
+ int mask;
+ float c[4];
+ float d;
+ quint32 s;
+ } clear;
+ struct {
+ float x, y, w, h;
+ float d0, d1;
+ } viewport;
+ struct {
+ int x, y, w, h;
+ } scissor;
+ struct {
+ int startSlot;
+ int slotCount;
+ ID3D11Buffer *buffers[D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT];
+ UINT offsets[D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT];
+ UINT strides[D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT];
+ } bindVertexBuffers;
+ struct {
+ ID3D11Buffer *buffer;
+ quint32 offset;
+ DXGI_FORMAT format;
+ } bindIndexBuffer;
+ struct {
+ QD3D11GraphicsPipeline *ps;
+ } bindGraphicsPipeline;
+ struct {
+ QD3D11ShaderResourceBindings *srb;
+ bool offsetOnlyChange;
+ int dynamicOffsetCount;
+ uint dynamicOffsetPairs[MAX_UBUF_BINDINGS * 2]; // binding, offsetInConstants
+ } bindShaderResources;
+ struct {
+ QD3D11GraphicsPipeline *ps;
+ quint32 ref;
+ } stencilRef;
+ struct {
+ QD3D11GraphicsPipeline *ps;
+ float c[4];
+ } blendConstants;
+ struct {
+ QD3D11GraphicsPipeline *ps;
+ quint32 vertexCount;
+ quint32 instanceCount;
+ quint32 firstVertex;
+ quint32 firstInstance;
+ } draw;
+ struct {
+ QD3D11GraphicsPipeline *ps;
+ quint32 indexCount;
+ quint32 instanceCount;
+ quint32 firstIndex;
+ qint32 vertexOffset;
+ quint32 firstInstance;
+ } drawIndexed;
+ struct {
+ ID3D11Resource *dst;
+ UINT dstSubRes;
+ bool hasDstBox;
+ D3D11_BOX dstBox;
+ const void *src; // must come from retain*()
+ UINT srcRowPitch;
+ } updateSubRes;
+ struct {
+ ID3D11Resource *dst;
+ UINT dstSubRes;
+ UINT dstX;
+ UINT dstY;
+ ID3D11Resource *src;
+ UINT srcSubRes;
+ bool hasSrcBox;
+ D3D11_BOX srcBox;
+ } copySubRes;
+ struct {
+ ID3D11Resource *dst;
+ UINT dstSubRes;
+ ID3D11Resource *src;
+ UINT srcSubRes;
+ DXGI_FORMAT format;
+ } resolveSubRes;
+ struct {
+ ID3D11ShaderResourceView *srv;
+ } genMip;
+ struct {
+ char s[64];
+ } debugMark;
+ } args;
+ };
+
+ QVector<Command> commands;
+ QRhiRenderTarget *currentTarget;
+ QRhiGraphicsPipeline *currentPipeline;
+ uint currentPipelineGeneration;
+ QRhiShaderResourceBindings *currentSrb;
+ uint currentSrbGeneration;
+ ID3D11Buffer *currentIndexBuffer;
+ quint32 currentIndexOffset;
+ DXGI_FORMAT currentIndexFormat;
+ ID3D11Buffer *currentVertexBuffers[D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT];
+ quint32 currentVertexOffsets[D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT];
+
+ QVector<QByteArray> dataRetainPool;
+ QVector<QImage> imageRetainPool;
+
+ // relies heavily on implicit sharing (no copies of the actual data will be made)
+ const uchar *retainData(const QByteArray &data) {
+ dataRetainPool.append(data);
+ return reinterpret_cast<const uchar *>(dataRetainPool.constLast().constData());
+ }
+ const uchar *retainImage(const QImage &image) {
+ imageRetainPool.append(image);
+ return imageRetainPool.constLast().constBits();
+ }
+ void resetCommands() {
+ commands.clear();
+ dataRetainPool.clear();
+ imageRetainPool.clear();
+ }
+ void resetState() {
+ resetCommands();
+ currentTarget = nullptr;
+ resetCachedState();
+ }
+ void resetCachedState() {
+ currentPipeline = nullptr;
+ currentPipelineGeneration = 0;
+ currentSrb = nullptr;
+ currentSrbGeneration = 0;
+ currentIndexBuffer = nullptr;
+ currentIndexOffset = 0;
+ currentIndexFormat = DXGI_FORMAT_R16_UINT;
+ memset(currentVertexBuffers, 0, sizeof(currentVertexBuffers));
+ memset(currentVertexOffsets, 0, sizeof(currentVertexOffsets));
+ }
+};
+
+Q_DECLARE_TYPEINFO(QD3D11CommandBuffer::Command, Q_MOVABLE_TYPE);
+
+struct QD3D11SwapChain : public QRhiSwapChain
+{
+ QD3D11SwapChain(QRhiImplementation *rhi);
+ ~QD3D11SwapChain();
+ void release() override;
+
+ QRhiCommandBuffer *currentFrameCommandBuffer() override;
+ QRhiRenderTarget *currentFrameRenderTarget() override;
+
+ QSize surfacePixelSize() override;
+
+ QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override;
+ bool buildOrResize() override;
+
+ void releaseBuffers();
+ bool newColorBuffer(const QSize &size, DXGI_FORMAT format, DXGI_SAMPLE_DESC sampleDesc,
+ ID3D11Texture2D **tex, ID3D11RenderTargetView **rtv) const;
+
+ QWindow *window = nullptr;
+ QSize pixelSize;
+ QD3D11ReferenceRenderTarget rt;
+ QD3D11CommandBuffer cb;
+ DXGI_FORMAT colorFormat;
+ IDXGISwapChain *swapChain = nullptr;
+ static const int BUFFER_COUNT = 2;
+ ID3D11Texture2D *tex[BUFFER_COUNT];
+ ID3D11RenderTargetView *rtv[BUFFER_COUNT];
+ ID3D11Texture2D *msaaTex[BUFFER_COUNT];
+ ID3D11RenderTargetView *msaaRtv[BUFFER_COUNT];
+ DXGI_SAMPLE_DESC sampleDesc;
+ int currentFrameSlot = 0;
+ int frameCount = 0;
+ QD3D11RenderBuffer *ds = nullptr;
+ bool timestampActive[BUFFER_COUNT];
+ ID3D11Query *timestampDisjointQuery[BUFFER_COUNT];
+ ID3D11Query *timestampQuery[BUFFER_COUNT * 2];
+ UINT swapInterval = 1;
+};
+
+class QRhiD3D11 : public QRhiImplementation
+{
+public:
+ QRhiD3D11(QRhiD3D11InitParams *params, QRhiD3D11NativeHandles *importDevice = nullptr);
+
+ bool create(QRhi::Flags flags) override;
+ void destroy() override;
+
+ QRhiGraphicsPipeline *createGraphicsPipeline() override;
+ QRhiShaderResourceBindings *createShaderResourceBindings() override;
+ QRhiBuffer *createBuffer(QRhiBuffer::Type type,
+ QRhiBuffer::UsageFlags usage,
+ int size) override;
+ QRhiRenderBuffer *createRenderBuffer(QRhiRenderBuffer::Type type,
+ const QSize &pixelSize,
+ int sampleCount,
+ QRhiRenderBuffer::Flags flags) override;
+ QRhiTexture *createTexture(QRhiTexture::Format format,
+ const QSize &pixelSize,
+ int sampleCount,
+ QRhiTexture::Flags flags) override;
+ QRhiSampler *createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
+ QRhiSampler::Filter mipmapMode,
+ QRhiSampler:: AddressMode u, QRhiSampler::AddressMode v) override;
+
+ QRhiTextureRenderTarget *createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
+ QRhiTextureRenderTarget::Flags flags) override;
+
+ QRhiSwapChain *createSwapChain() override;
+ QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override;
+ QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) override;
+ QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb) override;
+ QRhi::FrameOpResult endOffscreenFrame() override;
+ QRhi::FrameOpResult finish() override;
+
+ void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;
+
+ void beginPass(QRhiCommandBuffer *cb,
+ QRhiRenderTarget *rt,
+ const QColor &colorClearValue,
+ const QRhiDepthStencilClearValue &depthStencilClearValue,
+ QRhiResourceUpdateBatch *resourceUpdates) override;
+ void endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;
+
+ void setGraphicsPipeline(QRhiCommandBuffer *cb,
+ QRhiGraphicsPipeline *ps) override;
+
+ void setShaderResources(QRhiCommandBuffer *cb,
+ QRhiShaderResourceBindings *srb,
+ int dynamicOffsetCount,
+ const QRhiCommandBuffer::DynamicOffset *dynamicOffsets) override;
+
+ void setVertexInput(QRhiCommandBuffer *cb,
+ int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings,
+ QRhiBuffer *indexBuf, quint32 indexOffset,
+ QRhiCommandBuffer::IndexFormat indexFormat) override;
+
+ void setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport) override;
+ void setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) override;
+ void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) override;
+ void setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) override;
+
+ void draw(QRhiCommandBuffer *cb, quint32 vertexCount,
+ quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) override;
+
+ void drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
+ quint32 instanceCount, quint32 firstIndex,
+ qint32 vertexOffset, quint32 firstInstance) override;
+
+ void debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name) override;
+ void debugMarkEnd(QRhiCommandBuffer *cb) override;
+ void debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg) override;
+
+ const QRhiNativeHandles *nativeHandles(QRhiCommandBuffer *cb) override;
+ void beginExternal(QRhiCommandBuffer *cb) override;
+ void endExternal(QRhiCommandBuffer *cb) override;
+
+ QVector<int> supportedSampleCounts() const override;
+ int ubufAlignment() const override;
+ bool isYUpInFramebuffer() const override;
+ bool isYUpInNDC() const override;
+ bool isClipDepthZeroToOne() const override;
+ QMatrix4x4 clipSpaceCorrMatrix() const override;
+ bool isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const override;
+ bool isFeatureSupported(QRhi::Feature feature) const override;
+ int resourceLimit(QRhi::ResourceLimit limit) const override;
+ const QRhiNativeHandles *nativeHandles() override;
+ void sendVMemStatsToProfiler() override;
+
+ void flushCommandBuffer();
+ void enqueueSubresUpload(QD3D11Texture *texD, QD3D11CommandBuffer *cbD,
+ int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc);
+ void enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates);
+ void updateShaderResourceBindings(QD3D11ShaderResourceBindings *srbD);
+ void executeBufferHostWritesForCurrentFrame(QD3D11Buffer *bufD);
+ void bindShaderResources(QD3D11ShaderResourceBindings *srbD,
+ const uint *dynOfsPairs, int dynOfsPairCount,
+ bool offsetOnlyChange);
+ void setRenderTarget(QRhiRenderTarget *rt);
+ void executeCommandBuffer(QD3D11CommandBuffer *cbD, QD3D11SwapChain *timestampSwapChain = nullptr);
+ DXGI_SAMPLE_DESC effectiveSampleCount(int sampleCount) const;
+ void finishActiveReadbacks();
+ void enqueueSetRenderTarget(QD3D11CommandBuffer *cbD, QRhiRenderTarget *rt);
+ void reportLiveObjects(ID3D11Device *device);
+
+ bool debugLayer = false;
+ bool importedDevice = false;
+ ID3D11Device *dev = nullptr;
+ ID3D11DeviceContext1 *context = nullptr;
+ D3D_FEATURE_LEVEL featureLevel;
+ ID3DUserDefinedAnnotation *annotations = nullptr;
+ IDXGIFactory1 *dxgiFactory = nullptr;
+ bool hasDxgi2 = false;
+ QRhiD3D11NativeHandles nativeHandlesStruct;
+
+ bool inFrame = false;
+ bool inPass = false;
+
+ struct {
+ int vsHighestActiveSrvBinding = -1;
+ int fsHighestActiveSrvBinding = -1;
+ QD3D11SwapChain *currentSwapChain = nullptr;
+ } contextState;
+
+ struct OffscreenFrame {
+ OffscreenFrame(QRhiImplementation *rhi) : cbWrapper(rhi) { }
+ bool active = false;
+ QD3D11CommandBuffer cbWrapper;
+ } ofr;
+
+ struct ActiveReadback {
+ QRhiReadbackDescription desc;
+ QRhiReadbackResult *result;
+ ID3D11Texture2D *stagingTex;
+ quint32 bufSize;
+ quint32 bpl;
+ QSize pixelSize;
+ QRhiTexture::Format format;
+ };
+ QVector<ActiveReadback> activeReadbacks;
+};
+
+Q_DECLARE_TYPEINFO(QRhiD3D11::ActiveReadback, Q_MOVABLE_TYPE);
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/rhi/qrhigles2.cpp b/src/gui/rhi/qrhigles2.cpp
new file mode 100644
index 0000000000..0c89a9284f
--- /dev/null
+++ b/src/gui/rhi/qrhigles2.cpp
@@ -0,0 +1,2975 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gui module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qrhigles2_p_p.h"
+#include <QWindow>
+#include <QOffscreenSurface>
+#include <QOpenGLContext>
+#include <QtGui/private/qopenglextensions_p.h>
+#include <qmath.h>
+
+QT_BEGIN_NAMESPACE
+
+/*
+ OpenGL backend. Binding vertex attribute locations and decomposing uniform
+ buffers into uniforms are handled transparently to the application via the
+ reflection data (QShaderDescription). Real uniform buffers are never used,
+ regardless of the GLSL version. Textures and buffers feature no special logic,
+ it's all just glTexSubImage2D and glBufferSubData (with "dynamic" buffers set
+ to GL_DYNAMIC_DRAW). The swapchain and the associated renderbuffer for
+ depth-stencil will be dummies since we have no control over the underlying
+ buffers here. While we try to keep this backend clean GLES 2.0, some GL(ES)
+ 3.0 features like multisample renderbuffers and blits are used when available.
+*/
+
+/*!
+ \class QRhiGles2InitParams
+ \inmodule QtRhi
+ \brief OpenGL specific initialization parameters.
+
+ An OpenGL-based QRhi needs an already created QOffscreenSurface at minimum.
+ Additionally, while optional, it is recommended that the QWindow the first
+ QRhiSwapChain will target is passed in as well.
+
+ \badcode
+ QOffscreenSurface *fallbackSurface = QRhiGles2InitParams::newFallbackSurface();
+ QRhiGles2InitParams params;
+ params.fallbackSurface = fallbackSurface;
+ params.window = window;
+ rhi = QRhi::create(QRhi::OpenGLES2, &params);
+ \endcode
+
+ By default QRhi creates a QOpenGLContext on its own. This approach works
+ well in most cases, included threaded scenarios, where there is a dedicated
+ QRhi for each rendering thread. As there will be a QOpenGLContext for each
+ QRhi, the OpenGL context requirements (a context can only be current on one
+ thread) are satisfied. The implicitly created context is destroyed
+ automatically together with the QRhi.
+
+ The QSurfaceFormat for the context is specified in \l format. The
+ constructor sets this to QSurfaceFormat::defaultFormat() so applications
+ that use QSurfaceFormat::setDefaultFormat() do not need to set the format
+ again.
+
+ \note The depth and stencil buffer sizes are set automatically to 24 and 8
+ when no size was explicitly set for these buffers in \l format. As there
+ are possible adjustments to \l format, applications can use
+ adjustedFormat() to query the effective format that is passed to
+ QOpenGLContext::setFormat() internally.
+
+ A QOffscreenSurface has to be specified in \l fallbackSurface. In order to
+ prevent mistakes in threaded situations, this is never created
+ automatically by the QRhi since, like QWindow, QOffscreenSurface can only
+ be created on the gui/main thread.
+
+ As a convenience, applications can use newFallbackSurface() which creates
+ and returns a QOffscreenSurface that is compatible with the QOpenGLContext
+ that is going to be created by the QRhi afterwards. Note that the ownership
+ of the returned QOffscreenSurface is transferred to the caller and the QRhi
+ will not destroy it.
+
+ \note QRhiSwapChain can only target QWindow instances that have their
+ surface type set to QSurface::OpenGLSurface.
+
+ \note \l window is optional. It is recommended to specify it whenever
+ possible, in order to avoid problems on multi-adapter and multi-screen
+ systems. When \l window is not set, the very first
+ QOpenGLContext::makeCurrent() happens with \l fallbackSurface which may be
+ an invisible window on some platforms (for example, Windows) and that may
+ trigger unexpected problems in some cases.
+
+ \section2 Working with existing OpenGL contexts
+
+ When interoperating with another graphics engine, it may be necessary to
+ get a QRhi instance that uses the same OpenGL context. This can be achieved
+ by passing a pointer to a QRhiGles2NativeHandles to QRhi::create(). The
+ \l{QRhiGles2NativeHandles::context}{context} must be set to a non-null
+ value.
+
+ An alternative approach is to create a QOpenGLContext that
+ \l{QOpenGLContext::setShareContext()}{shares resources} with the other
+ engine's context and passing in that context via QRhiGles2NativeHandles.
+
+ The QRhi does not take ownership of the QOpenGLContext passed in via
+ QRhiGles2NativeHandles.
+ */
+
+/*!
+ \class QRhiGles2NativeHandles
+ \inmodule QtRhi
+ \brief Holds the OpenGL context used by the QRhi.
+ */
+
+/*!
+ \class QRhiGles2TextureNativeHandles
+ \inmodule QtRhi
+ \brief Holds the OpenGL texture object that is backing a QRhiTexture instance.
+ */
+
+#ifndef GL_BGRA
+#define GL_BGRA 0x80E1
+#endif
+
+#ifndef GL_R8
+#define GL_R8 0x8229
+#endif
+
+#ifndef GL_R16
+#define GL_R16 0x822A
+#endif
+
+#ifndef GL_RED
+#define GL_RED 0x1903
+#endif
+
+#ifndef GL_RGBA8
+#define GL_RGBA8 0x8058
+#endif
+
+#ifndef GL_RGBA32F
+#define GL_RGBA32F 0x8814
+#endif
+
+#ifndef GL_RGBA16F
+#define GL_RGBA16F 0x881A
+#endif
+
+#ifndef GL_HALF_FLOAT
+#define GL_HALF_FLOAT 0x140B
+#endif
+
+#ifndef GL_DEPTH_COMPONENT16
+#define GL_DEPTH_COMPONENT16 0x81A5
+#endif
+
+#ifndef GL_DEPTH_COMPONENT32F
+#define GL_DEPTH_COMPONENT32F 0x8CAC
+#endif
+
+#ifndef GL_DEPTH24_STENCIL8
+#define GL_DEPTH24_STENCIL8 0x88F0
+#endif
+
+#ifndef GL_PRIMITIVE_RESTART_FIXED_INDEX
+#define GL_PRIMITIVE_RESTART_FIXED_INDEX 0x8D69
+#endif
+
+#ifndef GL_FRAMEBUFFER_SRGB
+#define GL_FRAMEBUFFER_SRGB 0x8DB9
+#endif
+
+#ifndef GL_READ_FRAMEBUFFER
+#define GL_READ_FRAMEBUFFER 0x8CA8
+#endif
+
+#ifndef GL_DRAW_FRAMEBUFFER
+#define GL_DRAW_FRAMEBUFFER 0x8CA9
+#endif
+
+#ifndef GL_MAX_DRAW_BUFFERS
+#define GL_MAX_DRAW_BUFFERS 0x8824
+#endif
+
+#ifndef GL_TEXTURE_COMPARE_MODE
+#define GL_TEXTURE_COMPARE_MODE 0x884C
+#endif
+
+#ifndef GL_COMPARE_REF_TO_TEXTURE
+#define GL_COMPARE_REF_TO_TEXTURE 0x884E
+#endif
+
+#ifndef GL_TEXTURE_COMPARE_FUNC
+#define GL_TEXTURE_COMPARE_FUNC 0x884D
+#endif
+
+#ifndef GL_MAX_SAMPLES
+#define GL_MAX_SAMPLES 0x8D57
+#endif
+
+/*!
+ Constructs a new QRhiGles2InitParams.
+
+ \l format is set to QSurfaceFormat::defaultFormat().
+ */
+QRhiGles2InitParams::QRhiGles2InitParams()
+{
+ format = QSurfaceFormat::defaultFormat();
+}
+
+/*!
+ \return the QSurfaceFormat that will be set on the QOpenGLContext before
+ calling QOpenGLContext::create(). This format is based on \a format, but
+ may be adjusted. Applicable only when QRhi creates the context.
+ Applications are advised to set this format on their QWindow in order to
+ avoid potential BAD_MATCH failures.
+ */
+QSurfaceFormat QRhiGles2InitParams::adjustedFormat(const QSurfaceFormat &format)
+{
+ QSurfaceFormat fmt = format;
+
+ if (fmt.depthBufferSize() == -1)
+ fmt.setDepthBufferSize(24);
+ if (fmt.stencilBufferSize() == -1)
+ fmt.setStencilBufferSize(8);
+
+ return fmt;
+}
+
+/*!
+ \return a new QOffscreenSurface that can be used with a QRhi by passing it
+ via a QRhiGles2InitParams.
+
+ \a format is adjusted as appropriate in order to avoid having problems
+ afterwards due to an incompatible context and surface.
+
+ \note This function must only be called on the gui/main thread.
+
+ \note It is the application's responsibility to destroy the returned
+ QOffscreenSurface on the gui/main thread once the associated QRhi has been
+ destroyed. The QRhi will not destroy the QOffscreenSurface.
+ */
+QOffscreenSurface *QRhiGles2InitParams::newFallbackSurface(const QSurfaceFormat &format)
+{
+ QSurfaceFormat fmt = adjustedFormat(format);
+
+ // To resolve all fields in the format as much as possible, create a context.
+ // This may be heavy, but allows avoiding BAD_MATCH on some systems.
+ QOpenGLContext tempContext;
+ tempContext.setFormat(fmt);
+ if (tempContext.create())
+ fmt = tempContext.format();
+ else
+ qWarning("QRhiGles2: Failed to create temporary context");
+
+ QOffscreenSurface *s = new QOffscreenSurface;
+ s->setFormat(fmt);
+ s->create();
+
+ return s;
+}
+
+QRhiGles2::QRhiGles2(QRhiGles2InitParams *params, QRhiGles2NativeHandles *importDevice)
+ : ofr(this)
+{
+ requestedFormat = QRhiGles2InitParams::adjustedFormat(params->format);
+ fallbackSurface = params->fallbackSurface;
+ maybeWindow = params->window; // may be null
+
+ importedContext = importDevice != nullptr;
+ if (importedContext) {
+ ctx = importDevice->context;
+ if (!ctx) {
+ qWarning("No OpenGL context given, cannot import");
+ importedContext = false;
+ }
+ }
+}
+
+bool QRhiGles2::ensureContext(QSurface *surface) const
+{
+ bool nativeWindowGone = false;
+ if (surface && surface->surfaceClass() == QSurface::Window && !surface->surfaceHandle()) {
+ surface = fallbackSurface;
+ nativeWindowGone = true;
+ }
+
+ if (!surface)
+ surface = fallbackSurface;
+
+ if (needsMakeCurrent)
+ needsMakeCurrent = false;
+ else if (!nativeWindowGone && QOpenGLContext::currentContext() == ctx && (surface == fallbackSurface || ctx->surface() == surface))
+ return true;
+
+ if (!ctx->makeCurrent(surface)) {
+ qWarning("QRhiGles2: Failed to make context current. Expect bad things to happen.");
+ return false;
+ }
+
+ return true;
+}
+
+bool QRhiGles2::create(QRhi::Flags flags)
+{
+ Q_UNUSED(flags);
+ Q_ASSERT(fallbackSurface);
+
+ if (!importedContext) {
+ ctx = new QOpenGLContext;
+ ctx->setFormat(requestedFormat);
+ if (!ctx->create()) {
+ qWarning("QRhiGles2: Failed to create context");
+ delete ctx;
+ ctx = nullptr;
+ return false;
+ }
+ qDebug() << "Created OpenGL context" << ctx->format();
+ }
+
+ if (!ensureContext(maybeWindow ? maybeWindow : fallbackSurface)) // see 'window' discussion in QRhiGles2InitParams comments
+ return false;
+
+ f = static_cast<QOpenGLExtensions *>(ctx->extraFunctions());
+
+ const char *vendor = reinterpret_cast<const char *>(f->glGetString(GL_VENDOR));
+ const char *renderer = reinterpret_cast<const char *>(f->glGetString(GL_RENDERER));
+ const char *version = reinterpret_cast<const char *>(f->glGetString(GL_VERSION));
+ if (vendor && renderer && version)
+ qDebug("OpenGL VENDOR: %s RENDERER: %s VERSION: %s", vendor, renderer, version);
+
+ const QSurfaceFormat actualFormat = ctx->format();
+
+ caps.ctxMajor = actualFormat.majorVersion();
+ caps.ctxMinor = actualFormat.minorVersion();
+
+ GLint n = 0;
+ f->glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, &n);
+ supportedCompressedFormats.resize(n);
+ if (n > 0)
+ f->glGetIntegerv(GL_COMPRESSED_TEXTURE_FORMATS, supportedCompressedFormats.data());
+
+ f->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &caps.maxTextureSize);
+
+ if (caps.ctxMajor >= 3 || actualFormat.renderableType() == QSurfaceFormat::OpenGL) {
+ f->glGetIntegerv(GL_MAX_DRAW_BUFFERS, &caps.maxDrawBuffers);
+ f->glGetIntegerv(GL_MAX_SAMPLES, &caps.maxSamples);
+ caps.maxSamples = qMax(1, caps.maxSamples);
+ } else {
+ caps.maxDrawBuffers = 1;
+ caps.maxSamples = 1;
+ }
+
+ caps.msaaRenderBuffer = f->hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample)
+ && f->hasOpenGLExtension(QOpenGLExtensions::FramebufferBlit);
+
+ caps.npotTexture = f->hasOpenGLFeature(QOpenGLFunctions::NPOTTextures);
+ caps.npotTextureRepeat = f->hasOpenGLFeature(QOpenGLFunctions::NPOTTextureRepeat);
+
+ caps.gles = actualFormat.renderableType() == QSurfaceFormat::OpenGLES;
+ if (caps.gles)
+ caps.fixedIndexPrimitiveRestart = caps.ctxMajor >= 3;
+ else
+ caps.fixedIndexPrimitiveRestart = caps.ctxMajor > 4 || (caps.ctxMajor == 4 && caps.ctxMinor >= 3);
+
+ if (caps.fixedIndexPrimitiveRestart)
+ f->glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX);
+
+ caps.bgraExternalFormat = f->hasOpenGLExtension(QOpenGLExtensions::BGRATextureFormat);
+ caps.bgraInternalFormat = caps.bgraExternalFormat && caps.gles;
+ caps.r8Format = f->hasOpenGLFeature(QOpenGLFunctions::TextureRGFormats);
+ caps.r16Format = f->hasOpenGLExtension(QOpenGLExtensions::Sized16Formats);
+ caps.floatFormats = caps.ctxMajor >= 3;
+ caps.depthTexture = caps.ctxMajor >= 3;
+ caps.packedDepthStencil = f->hasOpenGLExtension(QOpenGLExtensions::PackedDepthStencil);
+ caps.srgbCapableDefaultFramebuffer = f->hasOpenGLExtension(QOpenGLExtensions::SRGBFrameBuffer);
+ caps.coreProfile = actualFormat.profile() == QSurfaceFormat::CoreProfile;
+ caps.uniformBuffers = caps.ctxMajor >= 3 && (caps.gles || caps.ctxMinor >= 1);
+ caps.elementIndexUint = f->hasOpenGLExtension(QOpenGLExtensions::ElementIndexUint);
+
+ nativeHandlesStruct.context = ctx;
+
+ return true;
+}
+
+void QRhiGles2::destroy()
+{
+ if (!f)
+ return;
+
+ ensureContext();
+ executeDeferredReleases();
+
+ if (!importedContext) {
+ delete ctx;
+ ctx = nullptr;
+ }
+
+ f = nullptr;
+}
+
+void QRhiGles2::executeDeferredReleases()
+{
+ for (int i = releaseQueue.count() - 1; i >= 0; --i) {
+ const QRhiGles2::DeferredReleaseEntry &e(releaseQueue[i]);
+ switch (e.type) {
+ case QRhiGles2::DeferredReleaseEntry::Buffer:
+ f->glDeleteBuffers(1, &e.buffer.buffer);
+ break;
+ case QRhiGles2::DeferredReleaseEntry::Pipeline:
+ f->glDeleteProgram(e.pipeline.program);
+ break;
+ case QRhiGles2::DeferredReleaseEntry::Texture:
+ f->glDeleteTextures(1, &e.texture.texture);
+ break;
+ case QRhiGles2::DeferredReleaseEntry::RenderBuffer:
+ f->glDeleteRenderbuffers(1, &e.renderbuffer.renderbuffer);
+ f->glDeleteRenderbuffers(1, &e.renderbuffer.renderbuffer2);
+ break;
+ case QRhiGles2::DeferredReleaseEntry::TextureRenderTarget:
+ f->glDeleteFramebuffers(1, &e.textureRenderTarget.framebuffer);
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ releaseQueue.removeAt(i);
+ }
+}
+
+QVector<int> QRhiGles2::supportedSampleCounts() const
+{
+ if (supportedSampleCountList.isEmpty()) {
+ // 1, 2, 4, 8, ...
+ for (int i = 1; i <= caps.maxSamples; i *= 2)
+ supportedSampleCountList.append(i);
+ }
+ return supportedSampleCountList;
+}
+
+int QRhiGles2::effectiveSampleCount(int sampleCount) const
+{
+ // Stay compatible with QSurfaceFormat and friends where samples == 0 means the same as 1.
+ const int s = qBound(1, sampleCount, 64);
+ if (!supportedSampleCounts().contains(s)) {
+ qWarning("Attempted to set unsupported sample count %d", sampleCount);
+ return 1;
+ }
+ return s;
+}
+
+QRhiSwapChain *QRhiGles2::createSwapChain()
+{
+ return new QGles2SwapChain(this);
+}
+
+QRhiBuffer *QRhiGles2::createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, int size)
+{
+ return new QGles2Buffer(this, type, usage, size);
+}
+
+int QRhiGles2::ubufAlignment() const
+{
+ return 256;
+}
+
+bool QRhiGles2::isYUpInFramebuffer() const
+{
+ return true;
+}
+
+bool QRhiGles2::isYUpInNDC() const
+{
+ return true;
+}
+
+bool QRhiGles2::isClipDepthZeroToOne() const
+{
+ return false;
+}
+
+QMatrix4x4 QRhiGles2::clipSpaceCorrMatrix() const
+{
+ return QMatrix4x4(); // identity
+}
+
+static inline GLenum toGlCompressedTextureFormat(QRhiTexture::Format format, QRhiTexture::Flags flags)
+{
+ const bool srgb = flags.testFlag(QRhiTexture::sRGB);
+ switch (format) {
+ case QRhiTexture::BC1:
+ return srgb ? 0x8C4C : 0x83F0;
+ case QRhiTexture::BC3:
+ return srgb ? 0x8C4E : 0x83F2;
+ case QRhiTexture::BC5:
+ return srgb ? 0x8C4F : 0x83F3;
+
+ case QRhiTexture::ETC2_RGB8:
+ return srgb ? 0x9275 : 0x9274;
+ case QRhiTexture::ETC2_RGB8A1:
+ return srgb ? 0x9277 : 0x9276;
+ case QRhiTexture::ETC2_RGBA8:
+ return srgb ? 0x9279 : 0x9278;
+
+ case QRhiTexture::ASTC_4x4:
+ return srgb ? 0x93D0 : 0x93B0;
+ case QRhiTexture::ASTC_5x4:
+ return srgb ? 0x93D1 : 0x93B1;
+ case QRhiTexture::ASTC_5x5:
+ return srgb ? 0x93D2 : 0x93B2;
+ case QRhiTexture::ASTC_6x5:
+ return srgb ? 0x93D3 : 0x93B3;
+ case QRhiTexture::ASTC_6x6:
+ return srgb ? 0x93D4 : 0x93B4;
+ case QRhiTexture::ASTC_8x5:
+ return srgb ? 0x93D5 : 0x93B5;
+ case QRhiTexture::ASTC_8x6:
+ return srgb ? 0x93D6 : 0x93B6;
+ case QRhiTexture::ASTC_8x8:
+ return srgb ? 0x93D7 : 0x93B7;
+ case QRhiTexture::ASTC_10x5:
+ return srgb ? 0x93D8 : 0x93B8;
+ case QRhiTexture::ASTC_10x6:
+ return srgb ? 0x93D9 : 0x93B9;
+ case QRhiTexture::ASTC_10x8:
+ return srgb ? 0x93DA : 0x93BA;
+ case QRhiTexture::ASTC_10x10:
+ return srgb ? 0x93DB : 0x93BB;
+ case QRhiTexture::ASTC_12x10:
+ return srgb ? 0x93DC : 0x93BC;
+ case QRhiTexture::ASTC_12x12:
+ return srgb ? 0x93DD : 0x93BD;
+
+ default:
+ return 0; // this is reachable, just return an invalid format
+ }
+}
+
+bool QRhiGles2::isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const
+{
+ if (isCompressedFormat(format))
+ return supportedCompressedFormats.contains(toGlCompressedTextureFormat(format, flags));
+
+ switch (format) {
+ case QRhiTexture::D16:
+ Q_FALLTHROUGH();
+ case QRhiTexture::D32F:
+ return caps.depthTexture;
+
+ case QRhiTexture::BGRA8:
+ return caps.bgraExternalFormat;
+
+ case QRhiTexture::R8:
+ return caps.r8Format;
+
+ case QRhiTexture::R16:
+ return caps.r16Format;
+
+ case QRhiTexture::RGBA16F:
+ Q_FALLTHROUGH();
+ case QRhiTexture::RGBA32F:
+ return caps.floatFormats;
+
+ default:
+ break;
+ }
+
+ return true;
+}
+
+bool QRhiGles2::isFeatureSupported(QRhi::Feature feature) const
+{
+ switch (feature) {
+ case QRhi::MultisampleTexture:
+ return false;
+ case QRhi::MultisampleRenderBuffer:
+ return caps.msaaRenderBuffer;
+ case QRhi::DebugMarkers:
+ return false;
+ case QRhi::Timestamps:
+ return false;
+ case QRhi::Instancing:
+ return false;
+ case QRhi::CustomInstanceStepRate:
+ return false;
+ case QRhi::PrimitiveRestart:
+ return caps.fixedIndexPrimitiveRestart;
+ case QRhi::NonDynamicUniformBuffers:
+ return true;
+ case QRhi::NonFourAlignedEffectiveIndexBufferOffset:
+ return true;
+ case QRhi::NPOTTextureRepeat:
+ return caps.npotTextureRepeat;
+ case QRhi::RedOrAlpha8IsRed:
+ return caps.coreProfile;
+ case QRhi::ElementIndexUint:
+ return caps.elementIndexUint;
+ default:
+ Q_UNREACHABLE();
+ return false;
+ }
+}
+
+int QRhiGles2::resourceLimit(QRhi::ResourceLimit limit) const
+{
+ switch (limit) {
+ case QRhi::TextureSizeMin:
+ return 1;
+ case QRhi::TextureSizeMax:
+ return caps.maxTextureSize;
+ case QRhi::MaxColorAttachments:
+ return caps.maxDrawBuffers;
+ case QRhi::FramesInFlight:
+ return 2; // dummy
+ default:
+ Q_UNREACHABLE();
+ return 0;
+ }
+}
+
+const QRhiNativeHandles *QRhiGles2::nativeHandles()
+{
+ return &nativeHandlesStruct;
+}
+
+void QRhiGles2::sendVMemStatsToProfiler()
+{
+ // nothing to do here
+}
+
+QRhiRenderBuffer *QRhiGles2::createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize,
+ int sampleCount, QRhiRenderBuffer::Flags flags)
+{
+ return new QGles2RenderBuffer(this, type, pixelSize, sampleCount, flags);
+}
+
+QRhiTexture *QRhiGles2::createTexture(QRhiTexture::Format format, const QSize &pixelSize,
+ int sampleCount, QRhiTexture::Flags flags)
+{
+ return new QGles2Texture(this, format, pixelSize, sampleCount, flags);
+}
+
+QRhiSampler *QRhiGles2::createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
+ QRhiSampler::Filter mipmapMode,
+ QRhiSampler::AddressMode u, QRhiSampler::AddressMode v)
+{
+ return new QGles2Sampler(this, magFilter, minFilter, mipmapMode, u, v);
+}
+
+QRhiTextureRenderTarget *QRhiGles2::createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
+ QRhiTextureRenderTarget::Flags flags)
+{
+ return new QGles2TextureRenderTarget(this, desc, flags);
+}
+
+QRhiGraphicsPipeline *QRhiGles2::createGraphicsPipeline()
+{
+ return new QGles2GraphicsPipeline(this);
+}
+
+QRhiShaderResourceBindings *QRhiGles2::createShaderResourceBindings()
+{
+ return new QGles2ShaderResourceBindings(this);
+}
+
+void QRhiGles2::setGraphicsPipeline(QRhiCommandBuffer *cb, QRhiGraphicsPipeline *ps)
+{
+ Q_ASSERT(inPass);
+
+ QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
+ QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, ps);
+ const bool pipelineChanged = cbD->currentPipeline != ps || cbD->currentPipelineGeneration != psD->generation;
+
+ if (pipelineChanged) {
+ cbD->currentPipeline = ps;
+ cbD->currentPipelineGeneration = psD->generation;
+
+ QGles2CommandBuffer::Command cmd;
+ cmd.cmd = QGles2CommandBuffer::Command::BindGraphicsPipeline;
+ cmd.args.bindGraphicsPipeline.ps = ps;
+ cbD->commands.append(cmd);
+ }
+}
+
+void QRhiGles2::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBindings *srb,
+ int dynamicOffsetCount,
+ const QRhiCommandBuffer::DynamicOffset *dynamicOffsets)
+{
+ Q_ASSERT(inPass);
+
+ QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
+ Q_ASSERT(cbD->currentPipeline);
+
+ if (!srb)
+ srb = QRHI_RES(QGles2GraphicsPipeline, cbD->currentPipeline)->m_shaderResourceBindings;
+
+ QGles2ShaderResourceBindings *srbD = QRHI_RES(QGles2ShaderResourceBindings, srb);
+ bool hasDynamicOffsetInSrb = false;
+ for (int i = 0, ie = srbD->m_bindings.count(); i != ie; ++i) {
+ const QRhiShaderResourceBindingPrivate *b = QRhiShaderResourceBindingPrivate::get(&srbD->m_bindings[i]);
+ switch (b->type) {
+ case QRhiShaderResourceBinding::UniformBuffer:
+ if (b->u.ubuf.hasDynamicOffset)
+ hasDynamicOffsetInSrb = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ const bool srbChanged = cbD->currentSrb != srb || cbD->currentSrbGeneration != srbD->generation;
+
+ if (srbChanged || hasDynamicOffsetInSrb) {
+ cbD->currentSrb = srb;
+ cbD->currentSrbGeneration = srbD->generation;
+
+ QGles2CommandBuffer::Command cmd;
+ cmd.cmd = QGles2CommandBuffer::Command::BindShaderResources;
+ cmd.args.bindShaderResources.ps = cbD->currentPipeline;
+ cmd.args.bindShaderResources.srb = srb;
+ cmd.args.bindShaderResources.dynamicOffsetCount = 0;
+ if (hasDynamicOffsetInSrb) {
+ if (dynamicOffsetCount < QGles2CommandBuffer::Command::MAX_UBUF_BINDINGS) {
+ cmd.args.bindShaderResources.dynamicOffsetCount = dynamicOffsetCount;
+ uint *p = cmd.args.bindShaderResources.dynamicOffsetPairs;
+ for (int i = 0; i < dynamicOffsetCount; ++i) {
+ const QRhiCommandBuffer::DynamicOffset &dynOfs(dynamicOffsets[i]);
+ *p++ = dynOfs.first;
+ *p++ = dynOfs.second;
+ }
+ } else {
+ qWarning("Too many dynamic offsets (%d, max is %d)",
+ dynamicOffsetCount, QGles2CommandBuffer::Command::MAX_UBUF_BINDINGS);
+ }
+ }
+ cbD->commands.append(cmd);
+ }
+}
+
+void QRhiGles2::setVertexInput(QRhiCommandBuffer *cb,
+ int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings,
+ QRhiBuffer *indexBuf, quint32 indexOffset, QRhiCommandBuffer::IndexFormat indexFormat)
+{
+ Q_ASSERT(inPass);
+ QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
+
+ for (int i = 0; i < bindingCount; ++i) {
+ QRhiBuffer *buf = bindings[i].first;
+ quint32 ofs = bindings[i].second;
+ QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, buf);
+ Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::VertexBuffer));
+ QGles2CommandBuffer::Command cmd;
+ cmd.cmd = QGles2CommandBuffer::Command::BindVertexBuffer;
+ cmd.args.bindVertexBuffer.ps = cbD->currentPipeline;
+ cmd.args.bindVertexBuffer.buffer = bufD->buffer;
+ cmd.args.bindVertexBuffer.offset = ofs;
+ cmd.args.bindVertexBuffer.binding = startBinding + i;
+ cbD->commands.append(cmd);
+ }
+
+ if (indexBuf) {
+ QGles2Buffer *ibufD = QRHI_RES(QGles2Buffer, indexBuf);
+ Q_ASSERT(ibufD->m_usage.testFlag(QRhiBuffer::IndexBuffer));
+ QGles2CommandBuffer::Command cmd;
+ cmd.cmd = QGles2CommandBuffer::Command::BindIndexBuffer;
+ cmd.args.bindIndexBuffer.buffer = ibufD->buffer;
+ cmd.args.bindIndexBuffer.offset = indexOffset;
+ cmd.args.bindIndexBuffer.type = indexFormat == QRhiCommandBuffer::IndexUInt16 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT;
+ cbD->commands.append(cmd);
+ }
+}
+
+void QRhiGles2::setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport)
+{
+ Q_ASSERT(inPass);
+ QGles2CommandBuffer::Command cmd;
+ cmd.cmd = QGles2CommandBuffer::Command::Viewport;
+ const std::array<float, 4> r = viewport.viewport();
+ cmd.args.viewport.x = qMax(0.0f, r[0]);
+ cmd.args.viewport.y = qMax(0.0f, r[1]);
+ cmd.args.viewport.w = r[2];
+ cmd.args.viewport.h = r[3];
+ cmd.args.viewport.d0 = viewport.minDepth();
+ cmd.args.viewport.d1 = viewport.maxDepth();
+ QRHI_RES(QGles2CommandBuffer, cb)->commands.append(cmd);
+}
+
+void QRhiGles2::setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor)
+{
+ Q_ASSERT(inPass);
+ QGles2CommandBuffer::Command cmd;
+ cmd.cmd = QGles2CommandBuffer::Command::Scissor;
+ const std::array<int, 4> r = scissor.scissor();
+ cmd.args.scissor.x = qMax(0, r[0]);
+ cmd.args.scissor.y = qMax(0, r[1]);
+ cmd.args.scissor.w = r[2];
+ cmd.args.scissor.h = r[3];
+ QRHI_RES(QGles2CommandBuffer, cb)->commands.append(cmd);
+}
+
+void QRhiGles2::setBlendConstants(QRhiCommandBuffer *cb, const QColor &c)
+{
+ Q_ASSERT(inPass);
+ QGles2CommandBuffer::Command cmd;
+ cmd.cmd = QGles2CommandBuffer::Command::BlendConstants;
+ cmd.args.blendConstants.r = c.redF();
+ cmd.args.blendConstants.g = c.greenF();
+ cmd.args.blendConstants.b = c.blueF();
+ cmd.args.blendConstants.a = c.alphaF();
+ QRHI_RES(QGles2CommandBuffer, cb)->commands.append(cmd);
+}
+
+void QRhiGles2::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue)
+{
+ Q_ASSERT(inPass);
+ QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
+
+ QGles2CommandBuffer::Command cmd;
+ cmd.cmd = QGles2CommandBuffer::Command::StencilRef;
+ cmd.args.stencilRef.ref = refValue;
+ cmd.args.stencilRef.ps = cbD->currentPipeline;
+ cbD->commands.append(cmd);
+}
+
+void QRhiGles2::draw(QRhiCommandBuffer *cb, quint32 vertexCount,
+ quint32 instanceCount, quint32 firstVertex, quint32 firstInstance)
+{
+ Q_ASSERT(inPass);
+ Q_UNUSED(instanceCount); // no instancing
+ Q_UNUSED(firstInstance);
+ QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
+
+ QGles2CommandBuffer::Command cmd;
+ cmd.cmd = QGles2CommandBuffer::Command::Draw;
+ cmd.args.draw.ps = cbD->currentPipeline;
+ cmd.args.draw.vertexCount = vertexCount;
+ cmd.args.draw.firstVertex = firstVertex;
+ cbD->commands.append(cmd);
+}
+
+void QRhiGles2::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
+ quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance)
+{
+ Q_ASSERT(inPass);
+ Q_UNUSED(instanceCount); // no instancing
+ Q_UNUSED(firstInstance);
+ Q_UNUSED(vertexOffset); // no glDrawElementsBaseVertex
+ QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
+
+ QGles2CommandBuffer::Command cmd;
+ cmd.cmd = QGles2CommandBuffer::Command::DrawIndexed;
+ cmd.args.drawIndexed.ps = cbD->currentPipeline;
+ cmd.args.drawIndexed.indexCount = indexCount;
+ cmd.args.drawIndexed.firstIndex = firstIndex;
+ cbD->commands.append(cmd);
+}
+
+void QRhiGles2::debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name)
+{
+ if (!debugMarkers)
+ return;
+
+ Q_UNUSED(cb);
+ Q_UNUSED(name);
+}
+
+void QRhiGles2::debugMarkEnd(QRhiCommandBuffer *cb)
+{
+ if (!debugMarkers)
+ return;
+
+ Q_UNUSED(cb);
+}
+
+void QRhiGles2::debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg)
+{
+ if (!debugMarkers)
+ return;
+
+ Q_UNUSED(cb);
+ Q_UNUSED(msg);
+}
+
+const QRhiNativeHandles *QRhiGles2::nativeHandles(QRhiCommandBuffer *cb)
+{
+ Q_UNUSED(cb);
+ return nullptr;
+}
+
+void QRhiGles2::beginExternal(QRhiCommandBuffer *cb)
+{
+ Q_ASSERT(inPass);
+ Q_UNUSED(cb);
+ flushCommandBuffer(); // also ensures the context is current
+}
+
+void QRhiGles2::endExternal(QRhiCommandBuffer *cb)
+{
+ Q_ASSERT(inPass);
+ QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
+ Q_ASSERT(cbD->currentTarget);
+ Q_ASSERT(cbD->commands.isEmpty());
+ cbD->resetCachedState();
+ enqueueBindFramebuffer(cbD->currentTarget, cbD);
+}
+
+static void addBoundaryCommand(QGles2CommandBuffer *cb, QGles2CommandBuffer::Command::Cmd type)
+{
+ QGles2CommandBuffer::Command cmd;
+ cmd.cmd = type;
+ cb->commands.append(cmd);
+}
+
+QRhi::FrameOpResult QRhiGles2::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags)
+{
+ Q_UNUSED(flags);
+ Q_ASSERT(!inFrame);
+
+ QGles2SwapChain *swapChainD = QRHI_RES(QGles2SwapChain, swapChain);
+ if (!ensureContext(swapChainD->surface))
+ return QRhi::FrameOpError;
+
+ inFrame = true;
+ currentSwapChain = swapChainD;
+
+ QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
+ QRHI_PROF_F(beginSwapChainFrame(swapChain));
+
+ executeDeferredReleases();
+ swapChainD->cb.resetState();
+
+ addBoundaryCommand(&swapChainD->cb, QGles2CommandBuffer::Command::BeginFrame);
+
+ return QRhi::FrameOpSuccess;
+}
+
+QRhi::FrameOpResult QRhiGles2::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags)
+{
+ Q_ASSERT(inFrame);
+ inFrame = false;
+
+ QGles2SwapChain *swapChainD = QRHI_RES(QGles2SwapChain, swapChain);
+ Q_ASSERT(currentSwapChain == swapChainD);
+
+ addBoundaryCommand(&swapChainD->cb, QGles2CommandBuffer::Command::EndFrame);
+
+ if (!ensureContext(swapChainD->surface))
+ return QRhi::FrameOpError;
+
+ executeCommandBuffer(&swapChainD->cb);
+
+ QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
+ // this must be done before the swap
+ QRHI_PROF_F(endSwapChainFrame(swapChain, swapChainD->frameCount + 1));
+
+ if (swapChainD->surface && !flags.testFlag(QRhi::SkipPresent)) {
+ ctx->swapBuffers(swapChainD->surface);
+ needsMakeCurrent = true;
+ } else {
+ f->glFlush();
+ }
+
+ swapChainD->frameCount += 1;
+ currentSwapChain = nullptr;
+ return QRhi::FrameOpSuccess;
+}
+
+QRhi::FrameOpResult QRhiGles2::beginOffscreenFrame(QRhiCommandBuffer **cb)
+{
+ Q_ASSERT(!inFrame);
+
+ if (!ensureContext())
+ return QRhi::FrameOpError;
+
+ inFrame = true;
+ ofr.active = true;
+
+ executeDeferredReleases();
+ ofr.cbWrapper.resetState();
+
+ addBoundaryCommand(&ofr.cbWrapper, QGles2CommandBuffer::Command::BeginFrame);
+ *cb = &ofr.cbWrapper;
+
+ return QRhi::FrameOpSuccess;
+}
+
+QRhi::FrameOpResult QRhiGles2::endOffscreenFrame()
+{
+ Q_ASSERT(inFrame && ofr.active);
+ inFrame = false;
+ ofr.active = false;
+
+ addBoundaryCommand(&ofr.cbWrapper, QGles2CommandBuffer::Command::EndFrame);
+
+ if (!ensureContext())
+ return QRhi::FrameOpError;
+
+ executeCommandBuffer(&ofr.cbWrapper);
+
+ return QRhi::FrameOpSuccess;
+}
+
+QRhi::FrameOpResult QRhiGles2::finish()
+{
+ Q_ASSERT(!inPass); // because that's what the QRhi docs say, even though not required by this backend
+ return inFrame ? flushCommandBuffer() : QRhi::FrameOpSuccess;
+}
+
+QRhi::FrameOpResult QRhiGles2::flushCommandBuffer()
+{
+ if (ofr.active) {
+ Q_ASSERT(!currentSwapChain);
+ if (!ensureContext())
+ return QRhi::FrameOpError;
+ executeCommandBuffer(&ofr.cbWrapper);
+ ofr.cbWrapper.resetCommands();
+ } else {
+ Q_ASSERT(currentSwapChain);
+ if (!ensureContext(currentSwapChain->surface))
+ return QRhi::FrameOpError;
+ executeCommandBuffer(&currentSwapChain->cb);
+ currentSwapChain->cb.resetCommands();
+ }
+ return QRhi::FrameOpSuccess;
+}
+
+void QRhiGles2::enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cbD,
+ int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc)
+{
+ const bool isCompressed = isCompressedFormat(texD->m_format);
+ const bool isCubeMap = texD->m_flags.testFlag(QRhiTexture::CubeMap);
+ const GLenum faceTargetBase = isCubeMap ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : texD->target;
+ const QPoint dp = subresDesc.destinationTopLeft();
+ const QByteArray rawData = subresDesc.data();
+ if (!subresDesc.image().isNull()) {
+ QImage img = subresDesc.image();
+ QSize size = img.size();
+ QGles2CommandBuffer::Command cmd;
+ cmd.cmd = QGles2CommandBuffer::Command::SubImage;
+ if (!subresDesc.sourceSize().isEmpty() || !subresDesc.sourceTopLeft().isNull()) {
+ const QPoint sp = subresDesc.sourceTopLeft();
+ if (!subresDesc.sourceSize().isEmpty())
+ size = subresDesc.sourceSize();
+ img = img.copy(sp.x(), sp.y(), size.width(), size.height());
+ }
+ cmd.args.subImage.target = texD->target;
+ cmd.args.subImage.texture = texD->texture;
+ cmd.args.subImage.faceTarget = faceTargetBase + layer;
+ cmd.args.subImage.level = level;
+ cmd.args.subImage.dx = dp.x();
+ cmd.args.subImage.dy = dp.y();
+ cmd.args.subImage.w = size.width();
+ cmd.args.subImage.h = size.height();
+ cmd.args.subImage.glformat = texD->glformat;
+ cmd.args.subImage.gltype = texD->gltype;
+ cmd.args.subImage.rowStartAlign = 4;
+ cmd.args.subImage.data = cbD->retainImage(img);
+ cbD->commands.append(cmd);
+ } else if (!rawData.isEmpty() && isCompressed) {
+ const QSize size = subresDesc.sourceSize().isEmpty() ? q->sizeForMipLevel(level, texD->m_pixelSize)
+ : subresDesc.sourceSize();
+ if (texD->specified) {
+ QGles2CommandBuffer::Command cmd;
+ cmd.cmd = QGles2CommandBuffer::Command::CompressedSubImage;
+ cmd.args.compressedSubImage.target = texD->target;
+ cmd.args.compressedSubImage.texture = texD->texture;
+ cmd.args.compressedSubImage.faceTarget = faceTargetBase + layer;
+ cmd.args.compressedSubImage.level = level;
+ cmd.args.compressedSubImage.dx = dp.x();
+ cmd.args.compressedSubImage.dy = dp.y();
+ cmd.args.compressedSubImage.w = size.width();
+ cmd.args.compressedSubImage.h = size.height();
+ cmd.args.compressedSubImage.glintformat = texD->glintformat;
+ cmd.args.compressedSubImage.size = rawData.size();
+ cmd.args.compressedSubImage.data = cbD->retainData(rawData);
+ cbD->commands.append(cmd);
+ } else {
+ QGles2CommandBuffer::Command cmd;
+ cmd.cmd = QGles2CommandBuffer::Command::CompressedImage;
+ cmd.args.compressedImage.target = texD->target;
+ cmd.args.compressedImage.texture = texD->texture;
+ cmd.args.compressedImage.faceTarget = faceTargetBase + layer;
+ cmd.args.compressedImage.level = level;
+ cmd.args.compressedImage.glintformat = texD->glintformat;
+ cmd.args.compressedImage.w = size.width();
+ cmd.args.compressedImage.h = size.height();
+ cmd.args.compressedImage.size = rawData.size();
+ cmd.args.compressedImage.data = cbD->retainData(rawData);
+ cbD->commands.append(cmd);
+ }
+ } else if (!rawData.isEmpty()) {
+ const QSize size = subresDesc.sourceSize().isEmpty() ? q->sizeForMipLevel(level, texD->m_pixelSize)
+ : subresDesc.sourceSize();
+ quint32 bpl = 0;
+ textureFormatInfo(texD->m_format, size, &bpl, nullptr);
+ QGles2CommandBuffer::Command cmd;
+ cmd.cmd = QGles2CommandBuffer::Command::SubImage;
+ cmd.args.subImage.target = texD->target;
+ cmd.args.subImage.texture = texD->texture;
+ cmd.args.subImage.faceTarget = faceTargetBase + layer;
+ cmd.args.subImage.level = level;
+ cmd.args.subImage.dx = dp.x();
+ cmd.args.subImage.dy = dp.y();
+ cmd.args.subImage.w = size.width();
+ cmd.args.subImage.h = size.height();
+ cmd.args.subImage.glformat = texD->glformat;
+ cmd.args.subImage.gltype = texD->gltype;
+ // Default unpack alignment (row start aligment
+ // requirement) is 4. QImage guarantees 4 byte aligned
+ // row starts, but our raw data here does not.
+ cmd.args.subImage.rowStartAlign = (bpl & 3) ? 1 : 4;
+ cmd.args.subImage.data = cbD->retainData(rawData);
+ cbD->commands.append(cmd);
+ } else {
+ qWarning("Invalid texture upload for %p layer=%d mip=%d", texD, layer, level);
+ }
+}
+
+void QRhiGles2::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
+ QRhiResourceUpdateBatchPrivate *ud = QRhiResourceUpdateBatchPrivate::get(resourceUpdates);
+
+ for (const QRhiResourceUpdateBatchPrivate::DynamicBufferUpdate &u : ud->dynamicBufferUpdates) {
+ QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, u.buf);
+ Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic);
+ if (bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer)) {
+ memcpy(bufD->ubuf.data() + u.offset, u.data.constData(), u.data.size());
+ } else {
+ QGles2CommandBuffer::Command cmd;
+ cmd.cmd = QGles2CommandBuffer::Command::BufferSubData;
+ cmd.args.bufferSubData.target = bufD->target;
+ cmd.args.bufferSubData.buffer = bufD->buffer;
+ cmd.args.bufferSubData.offset = u.offset;
+ cmd.args.bufferSubData.size = u.data.size();
+ cmd.args.bufferSubData.data = cbD->retainData(u.data);
+ cbD->commands.append(cmd);
+ }
+ }
+
+ for (const QRhiResourceUpdateBatchPrivate::StaticBufferUpload &u : ud->staticBufferUploads) {
+ QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, u.buf);
+ Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic);
+ Q_ASSERT(u.offset + u.data.size() <= bufD->m_size);
+ if (bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer)) {
+ memcpy(bufD->ubuf.data() + u.offset, u.data.constData(), u.data.size());
+ } else {
+ QGles2CommandBuffer::Command cmd;
+ cmd.cmd = QGles2CommandBuffer::Command::BufferSubData;
+ cmd.args.bufferSubData.target = bufD->target;
+ cmd.args.bufferSubData.buffer = bufD->buffer;
+ cmd.args.bufferSubData.offset = u.offset;
+ cmd.args.bufferSubData.size = u.data.size();
+ cmd.args.bufferSubData.data = cbD->retainData(u.data);
+ cbD->commands.append(cmd);
+ }
+ }
+
+ for (const QRhiResourceUpdateBatchPrivate::TextureOp &u : ud->textureOps) {
+ if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Upload) {
+ QGles2Texture *texD = QRHI_RES(QGles2Texture, u.upload.tex);
+ for (int layer = 0; layer < QRhi::MAX_LAYERS; ++layer) {
+ for (int level = 0; level < QRhi::MAX_LEVELS; ++level) {
+ for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.upload.subresDesc[layer][level]))
+ enqueueSubresUpload(texD, cbD, layer, level, subresDesc);
+ }
+ }
+ texD->specified = true;
+ } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Copy) {
+ Q_ASSERT(u.copy.src && u.copy.dst);
+ QGles2Texture *srcD = QRHI_RES(QGles2Texture, u.copy.src);
+ QGles2Texture *dstD = QRHI_RES(QGles2Texture, u.copy.dst);
+
+ const QSize size = u.copy.desc.pixelSize().isEmpty() ? srcD->m_pixelSize : u.copy.desc.pixelSize();
+ // do not translate coordinates, even if sp is bottom-left from gl's pov
+ const QPoint sp = u.copy.desc.sourceTopLeft();
+ const QPoint dp = u.copy.desc.destinationTopLeft();
+
+ const GLenum srcFaceTargetBase = srcD->m_flags.testFlag(QRhiTexture::CubeMap)
+ ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : srcD->target;
+ const GLenum dstFaceTargetBase = dstD->m_flags.testFlag(QRhiTexture::CubeMap)
+ ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : dstD->target;
+
+ QGles2CommandBuffer::Command cmd;
+ cmd.cmd = QGles2CommandBuffer::Command::CopyTex;
+
+ cmd.args.copyTex.srcFaceTarget = srcFaceTargetBase + u.copy.desc.sourceLayer();
+ cmd.args.copyTex.srcTexture = srcD->texture;
+ cmd.args.copyTex.srcLevel = u.copy.desc.sourceLevel();
+ cmd.args.copyTex.srcX = sp.x();
+ cmd.args.copyTex.srcY = sp.y();
+
+ cmd.args.copyTex.dstTarget = dstD->target;
+ cmd.args.copyTex.dstTexture = dstD->texture;
+ cmd.args.copyTex.dstFaceTarget = dstFaceTargetBase + u.copy.desc.destinationLayer();
+ cmd.args.copyTex.dstLevel = u.copy.desc.destinationLevel();
+ cmd.args.copyTex.dstX = dp.x();
+ cmd.args.copyTex.dstY = dp.y();
+
+ cmd.args.copyTex.w = size.width();
+ cmd.args.copyTex.h = size.height();
+
+ cbD->commands.append(cmd);
+ } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Read) {
+ QGles2CommandBuffer::Command cmd;
+ cmd.cmd = QGles2CommandBuffer::Command::ReadPixels;
+ cmd.args.readPixels.result = u.read.result;
+ QGles2Texture *texD = QRHI_RES(QGles2Texture, u.read.rb.texture());
+ cmd.args.readPixels.texture = texD ? texD->texture : 0;
+ if (texD) {
+ cmd.args.readPixels.w = texD->m_pixelSize.width();
+ cmd.args.readPixels.h = texD->m_pixelSize.height();
+ cmd.args.readPixels.format = texD->m_format;
+ const GLenum faceTargetBase = texD->m_flags.testFlag(QRhiTexture::CubeMap)
+ ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : texD->target;
+ cmd.args.readPixels.readTarget = faceTargetBase + u.read.rb.layer();
+ cmd.args.readPixels.level = u.read.rb.level();
+ }
+ cbD->commands.append(cmd);
+ } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::MipGen) {
+ QGles2CommandBuffer::Command cmd;
+ cmd.cmd = QGles2CommandBuffer::Command::GenMip;
+ QGles2Texture *texD = QRHI_RES(QGles2Texture, u.mipgen.tex);
+ cmd.args.genMip.target = texD->target;
+ cmd.args.genMip.texture = texD->texture;
+ cbD->commands.append(cmd);
+ }
+ }
+
+ ud->free();
+}
+
+static inline GLenum toGlTopology(QRhiGraphicsPipeline::Topology t)
+{
+ switch (t) {
+ case QRhiGraphicsPipeline::Triangles:
+ return GL_TRIANGLES;
+ case QRhiGraphicsPipeline::TriangleStrip:
+ return GL_TRIANGLE_STRIP;
+ case QRhiGraphicsPipeline::Lines:
+ return GL_LINES;
+ case QRhiGraphicsPipeline::LineStrip:
+ return GL_LINE_STRIP;
+ case QRhiGraphicsPipeline::Points:
+ return GL_POINTS;
+ default:
+ Q_UNREACHABLE();
+ return GL_TRIANGLES;
+ }
+}
+
+static inline GLenum toGlCullMode(QRhiGraphicsPipeline::CullMode c)
+{
+ switch (c) {
+ case QRhiGraphicsPipeline::Front:
+ return GL_FRONT;
+ case QRhiGraphicsPipeline::Back:
+ return GL_BACK;
+ default:
+ Q_UNREACHABLE();
+ return GL_BACK;
+ }
+}
+
+static inline GLenum toGlFrontFace(QRhiGraphicsPipeline::FrontFace f)
+{
+ switch (f) {
+ case QRhiGraphicsPipeline::CCW:
+ return GL_CCW;
+ case QRhiGraphicsPipeline::CW:
+ return GL_CW;
+ default:
+ Q_UNREACHABLE();
+ return GL_CCW;
+ }
+}
+
+static inline GLenum toGlBlendFactor(QRhiGraphicsPipeline::BlendFactor f)
+{
+ switch (f) {
+ case QRhiGraphicsPipeline::Zero:
+ return GL_ZERO;
+ case QRhiGraphicsPipeline::One:
+ return GL_ONE;
+ case QRhiGraphicsPipeline::SrcColor:
+ return GL_SRC_COLOR;
+ case QRhiGraphicsPipeline::OneMinusSrcColor:
+ return GL_ONE_MINUS_SRC_COLOR;
+ case QRhiGraphicsPipeline::DstColor:
+ return GL_DST_COLOR;
+ case QRhiGraphicsPipeline::OneMinusDstColor:
+ return GL_ONE_MINUS_DST_COLOR;
+ case QRhiGraphicsPipeline::SrcAlpha:
+ return GL_SRC_ALPHA;
+ case QRhiGraphicsPipeline::OneMinusSrcAlpha:
+ return GL_ONE_MINUS_SRC_ALPHA;
+ case QRhiGraphicsPipeline::DstAlpha:
+ return GL_DST_ALPHA;
+ case QRhiGraphicsPipeline::OneMinusDstAlpha:
+ return GL_ONE_MINUS_DST_ALPHA;
+ case QRhiGraphicsPipeline::ConstantColor:
+ return GL_CONSTANT_COLOR;
+ case QRhiGraphicsPipeline::OneMinusConstantColor:
+ return GL_ONE_MINUS_CONSTANT_COLOR;
+ case QRhiGraphicsPipeline::ConstantAlpha:
+ return GL_CONSTANT_ALPHA;
+ case QRhiGraphicsPipeline::OneMinusConstantAlpha:
+ return GL_ONE_MINUS_CONSTANT_ALPHA;
+ case QRhiGraphicsPipeline::SrcAlphaSaturate:
+ return GL_SRC_ALPHA_SATURATE;
+ case QRhiGraphicsPipeline::Src1Color:
+ Q_FALLTHROUGH();
+ case QRhiGraphicsPipeline::OneMinusSrc1Color:
+ Q_FALLTHROUGH();
+ case QRhiGraphicsPipeline::Src1Alpha:
+ Q_FALLTHROUGH();
+ case QRhiGraphicsPipeline::OneMinusSrc1Alpha:
+ qWarning("Unsupported blend factor %d", f);
+ return GL_ZERO;
+ default:
+ Q_UNREACHABLE();
+ return GL_ZERO;
+ }
+}
+
+static inline GLenum toGlBlendOp(QRhiGraphicsPipeline::BlendOp op)
+{
+ switch (op) {
+ case QRhiGraphicsPipeline::Add:
+ return GL_FUNC_ADD;
+ case QRhiGraphicsPipeline::Subtract:
+ return GL_FUNC_SUBTRACT;
+ case QRhiGraphicsPipeline::ReverseSubtract:
+ return GL_FUNC_REVERSE_SUBTRACT;
+ case QRhiGraphicsPipeline::Min:
+ return GL_MIN;
+ case QRhiGraphicsPipeline::Max:
+ return GL_MAX;
+ default:
+ Q_UNREACHABLE();
+ return GL_FUNC_ADD;
+ }
+}
+
+static inline GLenum toGlCompareOp(QRhiGraphicsPipeline::CompareOp op)
+{
+ switch (op) {
+ case QRhiGraphicsPipeline::Never:
+ return GL_NEVER;
+ case QRhiGraphicsPipeline::Less:
+ return GL_LESS;
+ case QRhiGraphicsPipeline::Equal:
+ return GL_EQUAL;
+ case QRhiGraphicsPipeline::LessOrEqual:
+ return GL_LEQUAL;
+ case QRhiGraphicsPipeline::Greater:
+ return GL_GREATER;
+ case QRhiGraphicsPipeline::NotEqual:
+ return GL_NOTEQUAL;
+ case QRhiGraphicsPipeline::GreaterOrEqual:
+ return GL_GEQUAL;
+ case QRhiGraphicsPipeline::Always:
+ return GL_ALWAYS;
+ default:
+ Q_UNREACHABLE();
+ return GL_ALWAYS;
+ }
+}
+
+static inline GLenum toGlStencilOp(QRhiGraphicsPipeline::StencilOp op)
+{
+ switch (op) {
+ case QRhiGraphicsPipeline::StencilZero:
+ return GL_ZERO;
+ case QRhiGraphicsPipeline::Keep:
+ return GL_KEEP;
+ case QRhiGraphicsPipeline::Replace:
+ return GL_REPLACE;
+ case QRhiGraphicsPipeline::IncrementAndClamp:
+ return GL_INCR;
+ case QRhiGraphicsPipeline::DecrementAndClamp:
+ return GL_DECR;
+ case QRhiGraphicsPipeline::Invert:
+ return GL_INVERT;
+ case QRhiGraphicsPipeline::IncrementAndWrap:
+ return GL_INCR_WRAP;
+ case QRhiGraphicsPipeline::DecrementAndWrap:
+ return GL_DECR_WRAP;
+ default:
+ Q_UNREACHABLE();
+ return GL_KEEP;
+ }
+}
+
+static inline GLenum toGlMinFilter(QRhiSampler::Filter f, QRhiSampler::Filter m)
+{
+ switch (f) {
+ case QRhiSampler::Nearest:
+ if (m == QRhiSampler::None)
+ return GL_NEAREST;
+ else
+ return m == QRhiSampler::Nearest ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST_MIPMAP_LINEAR;
+ case QRhiSampler::Linear:
+ if (m == QRhiSampler::None)
+ return GL_LINEAR;
+ else
+ return m == QRhiSampler::Nearest ? GL_LINEAR_MIPMAP_NEAREST : GL_LINEAR_MIPMAP_LINEAR;
+ default:
+ Q_UNREACHABLE();
+ return GL_LINEAR;
+ }
+}
+
+static inline GLenum toGlMagFilter(QRhiSampler::Filter f)
+{
+ switch (f) {
+ case QRhiSampler::Nearest:
+ return GL_NEAREST;
+ case QRhiSampler::Linear:
+ return GL_LINEAR;
+ default:
+ Q_UNREACHABLE();
+ return GL_LINEAR;
+ }
+}
+
+static inline GLenum toGlWrapMode(QRhiSampler::AddressMode m)
+{
+ switch (m) {
+ case QRhiSampler::Repeat:
+ return GL_REPEAT;
+ case QRhiSampler::ClampToEdge:
+ return GL_CLAMP_TO_EDGE;
+ case QRhiSampler::Mirror:
+ return GL_MIRRORED_REPEAT;
+ case QRhiSampler::MirrorOnce:
+ Q_FALLTHROUGH();
+ case QRhiSampler::Border:
+ qWarning("Unsupported wrap mode %d", m);
+ return GL_CLAMP_TO_EDGE;
+ default:
+ Q_UNREACHABLE();
+ return GL_CLAMP_TO_EDGE;
+ }
+}
+
+static inline GLenum toGlTextureCompareFunc(QRhiSampler::CompareOp op)
+{
+ switch (op) {
+ case QRhiSampler::Never:
+ return GL_NEVER;
+ case QRhiSampler::Less:
+ return GL_LESS;
+ case QRhiSampler::Equal:
+ return GL_EQUAL;
+ case QRhiSampler::LessOrEqual:
+ return GL_LEQUAL;
+ case QRhiSampler::Greater:
+ return GL_GREATER;
+ case QRhiSampler::NotEqual:
+ return GL_NOTEQUAL;
+ case QRhiSampler::GreaterOrEqual:
+ return GL_GEQUAL;
+ case QRhiSampler::Always:
+ return GL_ALWAYS;
+ default:
+ Q_UNREACHABLE();
+ return GL_NEVER;
+ }
+}
+
+void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
+{
+ QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
+ GLenum indexType = GL_UNSIGNED_SHORT;
+ quint32 indexStride = sizeof(quint16);
+ quint32 indexOffset = 0;
+
+ for (const QGles2CommandBuffer::Command &cmd : qAsConst(cbD->commands)) {
+ switch (cmd.cmd) {
+ case QGles2CommandBuffer::Command::BeginFrame:
+ if (caps.coreProfile) {
+ if (!vao)
+ f->glGenVertexArrays(1, &vao);
+ f->glBindVertexArray(vao);
+ }
+ break;
+ case QGles2CommandBuffer::Command::EndFrame:
+ if (vao)
+ f->glBindVertexArray(0);
+ break;
+ case QGles2CommandBuffer::Command::Viewport:
+ f->glViewport(cmd.args.viewport.x, cmd.args.viewport.y, cmd.args.viewport.w, cmd.args.viewport.h);
+ f->glDepthRangef(cmd.args.viewport.d0, cmd.args.viewport.d1);
+ break;
+ case QGles2CommandBuffer::Command::Scissor:
+ f->glScissor(cmd.args.scissor.x, cmd.args.scissor.y, cmd.args.scissor.w, cmd.args.scissor.h);
+ break;
+ case QGles2CommandBuffer::Command::BlendConstants:
+ f->glBlendColor(cmd.args.blendConstants.r, cmd.args.blendConstants.g, cmd.args.blendConstants.b, cmd.args.blendConstants.a);
+ break;
+ case QGles2CommandBuffer::Command::StencilRef:
+ {
+ QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, cmd.args.stencilRef.ps);
+ if (psD) {
+ f->glStencilFuncSeparate(GL_FRONT, toGlCompareOp(psD->m_stencilFront.compareOp), cmd.args.stencilRef.ref, psD->m_stencilReadMask);
+ f->glStencilFuncSeparate(GL_BACK, toGlCompareOp(psD->m_stencilBack.compareOp), cmd.args.stencilRef.ref, psD->m_stencilReadMask);
+ } else {
+ qWarning("No graphics pipeline active for setStencilRef; ignored");
+ }
+ }
+ break;
+ case QGles2CommandBuffer::Command::BindVertexBuffer:
+ {
+ QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, cmd.args.bindVertexBuffer.ps);
+ if (psD) {
+ const QVector<QRhiVertexInputBinding> bindings = psD->m_vertexInputLayout.bindings();
+ const QVector<QRhiVertexInputAttribute> attributes = psD->m_vertexInputLayout.attributes();
+ for (const QRhiVertexInputAttribute &a : attributes) {
+ if (a.binding() != cmd.args.bindVertexBuffer.binding)
+ continue;
+
+ // we do not support more than one vertex buffer
+ f->glBindBuffer(GL_ARRAY_BUFFER, cmd.args.bindVertexBuffer.buffer);
+
+ const int stride = bindings[a.binding()].stride();
+ int size = 1;
+ GLenum type = GL_FLOAT;
+ bool normalize = false;
+ switch (a.format()) {
+ case QRhiVertexInputAttribute::Float4:
+ type = GL_FLOAT;
+ size = 4;
+ break;
+ case QRhiVertexInputAttribute::Float3:
+ type = GL_FLOAT;
+ size = 3;
+ break;
+ case QRhiVertexInputAttribute::Float2:
+ type = GL_FLOAT;
+ size = 2;
+ break;
+ case QRhiVertexInputAttribute::Float:
+ type = GL_FLOAT;
+ size = 1;
+ break;
+ case QRhiVertexInputAttribute::UNormByte4:
+ type = GL_UNSIGNED_BYTE;
+ normalize = true;
+ size = 4;
+ break;
+ case QRhiVertexInputAttribute::UNormByte2:
+ type = GL_UNSIGNED_BYTE;
+ normalize = true;
+ size = 2;
+ break;
+ case QRhiVertexInputAttribute::UNormByte:
+ type = GL_UNSIGNED_BYTE;
+ normalize = true;
+ size = 1;
+ break;
+ default:
+ break;
+ }
+ quint32 ofs = a.offset() + cmd.args.bindVertexBuffer.offset;
+ f->glVertexAttribPointer(a.location(), size, type, normalize, stride,
+ reinterpret_cast<const GLvoid *>(quintptr(ofs)));
+ f->glEnableVertexAttribArray(a.location());
+ }
+ } else {
+ qWarning("No graphics pipeline active for setVertexInput; ignored");
+ }
+ }
+ break;
+ case QGles2CommandBuffer::Command::BindIndexBuffer:
+ indexType = cmd.args.bindIndexBuffer.type;
+ indexStride = indexType == GL_UNSIGNED_SHORT ? sizeof(quint16) : sizeof(quint32);
+ indexOffset = cmd.args.bindIndexBuffer.offset;
+ f->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cmd.args.bindIndexBuffer.buffer);
+ break;
+ case QGles2CommandBuffer::Command::Draw:
+ {
+ QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, cmd.args.draw.ps);
+ if (psD)
+ f->glDrawArrays(psD->drawMode, cmd.args.draw.firstVertex, cmd.args.draw.vertexCount);
+ else
+ qWarning("No graphics pipeline active for draw; ignored");
+ }
+ break;
+ case QGles2CommandBuffer::Command::DrawIndexed:
+ {
+ QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, cmd.args.drawIndexed.ps);
+ if (psD) {
+ quint32 ofs = cmd.args.drawIndexed.firstIndex * indexStride + indexOffset;
+ f->glDrawElements(psD->drawMode,
+ cmd.args.drawIndexed.indexCount,
+ indexType,
+ reinterpret_cast<const GLvoid *>(quintptr(ofs)));
+ } else {
+ qWarning("No graphics pipeline active for drawIndexed; ignored");
+ }
+ }
+ break;
+ case QGles2CommandBuffer::Command::BindGraphicsPipeline:
+ executeBindGraphicsPipeline(cmd.args.bindGraphicsPipeline.ps);
+ break;
+ case QGles2CommandBuffer::Command::BindShaderResources:
+ bindShaderResources(cmd.args.bindShaderResources.ps,
+ cmd.args.bindShaderResources.srb,
+ cmd.args.bindShaderResources.dynamicOffsetPairs,
+ cmd.args.bindShaderResources.dynamicOffsetCount);
+ break;
+ case QGles2CommandBuffer::Command::BindFramebuffer:
+ if (cmd.args.bindFramebuffer.fbo) {
+ f->glBindFramebuffer(GL_FRAMEBUFFER, cmd.args.bindFramebuffer.fbo);
+ if (caps.maxDrawBuffers > 1) {
+ const int colorAttCount = cmd.args.bindFramebuffer.colorAttCount;
+ QVarLengthArray<GLenum, 8> bufs;
+ for (int i = 0; i < colorAttCount; ++i)
+ bufs.append(GL_COLOR_ATTACHMENT0 + i);
+ f->glDrawBuffers(colorAttCount, bufs.constData());
+ }
+ } else {
+ f->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject());
+ if (caps.maxDrawBuffers > 1) {
+ GLenum bufs = GL_BACK;
+ f->glDrawBuffers(1, &bufs);
+ }
+ }
+ if (caps.srgbCapableDefaultFramebuffer) {
+ if (cmd.args.bindFramebuffer.srgb)
+ f->glEnable(GL_FRAMEBUFFER_SRGB);
+ else
+ f->glDisable(GL_FRAMEBUFFER_SRGB);
+ }
+ break;
+ case QGles2CommandBuffer::Command::Clear:
+ f->glDisable(GL_SCISSOR_TEST);
+ if (cmd.args.clear.mask & GL_COLOR_BUFFER_BIT) {
+ f->glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ f->glClearColor(cmd.args.clear.c[0], cmd.args.clear.c[1], cmd.args.clear.c[2], cmd.args.clear.c[3]);
+ }
+ if (cmd.args.clear.mask & GL_DEPTH_BUFFER_BIT) {
+ f->glDepthMask(GL_TRUE);
+ f->glClearDepthf(cmd.args.clear.d);
+ }
+ if (cmd.args.clear.mask & GL_STENCIL_BUFFER_BIT)
+ f->glClearStencil(cmd.args.clear.s);
+ f->glClear(cmd.args.clear.mask);
+ break;
+ case QGles2CommandBuffer::Command::BufferSubData:
+ f->glBindBuffer(cmd.args.bufferSubData.target, cmd.args.bufferSubData.buffer);
+ f->glBufferSubData(cmd.args.bufferSubData.target, cmd.args.bufferSubData.offset, cmd.args.bufferSubData.size,
+ cmd.args.bufferSubData.data);
+ break;
+ case QGles2CommandBuffer::Command::CopyTex:
+ {
+ GLuint fbo;
+ f->glGenFramebuffers(1, &fbo);
+ f->glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+ f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ cmd.args.copyTex.srcFaceTarget, cmd.args.copyTex.srcTexture, cmd.args.copyTex.srcLevel);
+ f->glBindTexture(cmd.args.copyTex.dstTarget, cmd.args.copyTex.dstTexture);
+ f->glCopyTexSubImage2D(cmd.args.copyTex.dstFaceTarget, cmd.args.copyTex.dstLevel,
+ cmd.args.copyTex.dstX, cmd.args.copyTex.dstY,
+ cmd.args.copyTex.srcX, cmd.args.copyTex.srcY,
+ cmd.args.copyTex.w, cmd.args.copyTex.h);
+ f->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject());
+ f->glDeleteFramebuffers(1, &fbo);
+ }
+ break;
+ case QGles2CommandBuffer::Command::ReadPixels:
+ {
+ QRhiReadbackResult *result = cmd.args.readPixels.result;
+ GLuint tex = cmd.args.readPixels.texture;
+ GLuint fbo = 0;
+ if (tex) {
+ result->pixelSize = QSize(cmd.args.readPixels.w, cmd.args.readPixels.h);
+ result->format = cmd.args.readPixels.format;
+ f->glGenFramebuffers(1, &fbo);
+ f->glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+ f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ cmd.args.readPixels.readTarget, cmd.args.readPixels.texture, cmd.args.readPixels.level);
+ } else {
+ result->pixelSize = currentSwapChain->pixelSize;
+ result->format = QRhiTexture::RGBA8;
+ // readPixels handles multisample resolving implicitly
+ }
+ result->data.resize(result->pixelSize.width() * result->pixelSize.height() * 4);
+ // With GLES (2.0?) GL_RGBA is the only mandated readback format, so stick with it.
+ f->glReadPixels(0, 0, result->pixelSize.width(), result->pixelSize.height(),
+ GL_RGBA, GL_UNSIGNED_BYTE,
+ result->data.data());
+ if (fbo) {
+ f->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject());
+ f->glDeleteFramebuffers(1, &fbo);
+ }
+ if (result->completed)
+ result->completed();
+ }
+ break;
+ case QGles2CommandBuffer::Command::SubImage:
+ f->glBindTexture(cmd.args.subImage.target, cmd.args.subImage.texture);
+ if (cmd.args.subImage.rowStartAlign != 4)
+ f->glPixelStorei(GL_UNPACK_ALIGNMENT, cmd.args.subImage.rowStartAlign);
+ f->glTexSubImage2D(cmd.args.subImage.faceTarget, cmd.args.subImage.level,
+ cmd.args.subImage.dx, cmd.args.subImage.dy,
+ cmd.args.subImage.w, cmd.args.subImage.h,
+ cmd.args.subImage.glformat, cmd.args.subImage.gltype,
+ cmd.args.subImage.data);
+ if (cmd.args.subImage.rowStartAlign != 4)
+ f->glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
+ break;
+ case QGles2CommandBuffer::Command::CompressedImage:
+ f->glBindTexture(cmd.args.compressedImage.target, cmd.args.compressedImage.texture);
+ f->glCompressedTexImage2D(cmd.args.compressedImage.faceTarget, cmd.args.compressedImage.level,
+ cmd.args.compressedImage.glintformat,
+ cmd.args.compressedImage.w, cmd.args.compressedImage.h, 0,
+ cmd.args.compressedImage.size, cmd.args.compressedImage.data);
+ break;
+ case QGles2CommandBuffer::Command::CompressedSubImage:
+ f->glBindTexture(cmd.args.compressedSubImage.target, cmd.args.compressedSubImage.texture);
+ f->glCompressedTexSubImage2D(cmd.args.compressedSubImage.faceTarget, cmd.args.compressedSubImage.level,
+ cmd.args.compressedSubImage.dx, cmd.args.compressedSubImage.dy,
+ cmd.args.compressedSubImage.w, cmd.args.compressedSubImage.h,
+ cmd.args.compressedSubImage.glintformat,
+ cmd.args.compressedSubImage.size, cmd.args.compressedSubImage.data);
+ break;
+ case QGles2CommandBuffer::Command::BlitFromRenderbuffer:
+ {
+ GLuint fbo[2];
+ f->glGenFramebuffers(2, fbo);
+ f->glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo[0]);
+ f->glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ GL_RENDERBUFFER, cmd.args.blitFromRb.renderbuffer);
+ f->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo[1]);
+
+ f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, cmd.args.blitFromRb.target,
+ cmd.args.blitFromRb.texture, cmd.args.blitFromRb.dstLevel);
+ f->glBlitFramebuffer(0, 0, cmd.args.blitFromRb.w, cmd.args.blitFromRb.h,
+ 0, 0, cmd.args.blitFromRb.w, cmd.args.blitFromRb.h,
+ GL_COLOR_BUFFER_BIT,
+ GL_LINEAR);
+ f->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject());
+ }
+ break;
+ case QGles2CommandBuffer::Command::GenMip:
+ f->glBindTexture(cmd.args.genMip.target, cmd.args.genMip.texture);
+ f->glGenerateMipmap(cmd.args.genMip.target);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void QRhiGles2::executeBindGraphicsPipeline(QRhiGraphicsPipeline *ps)
+{
+ QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, ps);
+
+ // No state tracking logic as of now. Could introduce something to reduce
+ // the number of gl* calls (when using and changing between multiple
+ // pipelines), but then begin/endExternal() should invalidate the cached
+ // state as appropriate.
+
+ if (psD->m_flags.testFlag(QRhiGraphicsPipeline::UsesScissor))
+ f->glEnable(GL_SCISSOR_TEST);
+ else
+ f->glDisable(GL_SCISSOR_TEST);
+ if (psD->m_cullMode == QRhiGraphicsPipeline::None) {
+ f->glDisable(GL_CULL_FACE);
+ } else {
+ f->glEnable(GL_CULL_FACE);
+ f->glCullFace(toGlCullMode(psD->m_cullMode));
+ }
+ f->glFrontFace(toGlFrontFace(psD->m_frontFace));
+ if (!psD->m_targetBlends.isEmpty()) {
+ const QRhiGraphicsPipeline::TargetBlend &blend(psD->m_targetBlends.first()); // no MRT
+ GLboolean wr = blend.colorWrite.testFlag(QRhiGraphicsPipeline::R);
+ GLboolean wg = blend.colorWrite.testFlag(QRhiGraphicsPipeline::G);
+ GLboolean wb = blend.colorWrite.testFlag(QRhiGraphicsPipeline::B);
+ GLboolean wa = blend.colorWrite.testFlag(QRhiGraphicsPipeline::A);
+ f->glColorMask(wr, wg, wb, wa);
+ if (blend.enable) {
+ f->glEnable(GL_BLEND);
+ f->glBlendFuncSeparate(toGlBlendFactor(blend.srcColor),
+ toGlBlendFactor(blend.dstColor),
+ toGlBlendFactor(blend.srcAlpha),
+ toGlBlendFactor(blend.dstAlpha));
+ f->glBlendEquationSeparate(toGlBlendOp(blend.opColor), toGlBlendOp(blend.opAlpha));
+ } else {
+ f->glDisable(GL_BLEND);
+ }
+ } else {
+ f->glDisable(GL_BLEND);
+ }
+ if (psD->m_depthTest)
+ f->glEnable(GL_DEPTH_TEST);
+ else
+ f->glDisable(GL_DEPTH_TEST);
+ if (psD->m_depthWrite)
+ f->glDepthMask(GL_TRUE);
+ else
+ f->glDepthMask(GL_FALSE);
+ f->glDepthFunc(toGlCompareOp(psD->m_depthOp));
+ if (psD->m_stencilTest) {
+ f->glEnable(GL_STENCIL_TEST);
+ f->glStencilFuncSeparate(GL_FRONT, toGlCompareOp(psD->m_stencilFront.compareOp), 0, psD->m_stencilReadMask);
+ f->glStencilOpSeparate(GL_FRONT,
+ toGlStencilOp(psD->m_stencilFront.failOp),
+ toGlStencilOp(psD->m_stencilFront.depthFailOp),
+ toGlStencilOp(psD->m_stencilFront.passOp));
+ f->glStencilMaskSeparate(GL_FRONT, psD->m_stencilWriteMask);
+ f->glStencilFuncSeparate(GL_BACK, toGlCompareOp(psD->m_stencilBack.compareOp), 0, psD->m_stencilReadMask);
+ f->glStencilOpSeparate(GL_BACK,
+ toGlStencilOp(psD->m_stencilBack.failOp),
+ toGlStencilOp(psD->m_stencilBack.depthFailOp),
+ toGlStencilOp(psD->m_stencilBack.passOp));
+ f->glStencilMaskSeparate(GL_BACK, psD->m_stencilWriteMask);
+ } else {
+ f->glDisable(GL_STENCIL_TEST);
+ }
+
+ f->glUseProgram(psD->program);
+}
+
+void QRhiGles2::bindShaderResources(QRhiGraphicsPipeline *ps, QRhiShaderResourceBindings *srb,
+ const uint *dynOfsPairs, int dynOfsCount)
+{
+ QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, ps);
+ QGles2ShaderResourceBindings *srbD = QRHI_RES(QGles2ShaderResourceBindings, srb);
+ int texUnit = 0;
+
+ for (int i = 0, ie = srbD->m_bindings.count(); i != ie; ++i) {
+ const QRhiShaderResourceBindingPrivate *b = QRhiShaderResourceBindingPrivate::get(&srbD->m_bindings[i]);
+
+ switch (b->type) {
+ case QRhiShaderResourceBinding::UniformBuffer:
+ {
+ int viewOffset = b->u.ubuf.offset;
+ if (dynOfsCount) {
+ for (int j = 0; j < dynOfsCount; ++j) {
+ if (dynOfsPairs[2 * j] == uint(b->binding)) {
+ viewOffset = dynOfsPairs[2 * j + 1];
+ break;
+ }
+ }
+ }
+ QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, b->u.ubuf.buf);
+ const QByteArray bufView = QByteArray::fromRawData(bufD->ubuf.constData() + viewOffset,
+ b->u.ubuf.maybeSize ? b->u.ubuf.maybeSize : bufD->m_size);
+ for (QGles2GraphicsPipeline::Uniform &uniform : psD->uniforms) {
+ if (uniform.binding == b->binding) {
+ // in a uniform buffer everything is at least 4 byte aligned
+ // so this should not cause unaligned reads
+ const void *src = bufView.constData() + uniform.offset;
+
+ switch (uniform.type) {
+ case QShaderDescription::Float:
+ f->glUniform1f(uniform.glslLocation, *reinterpret_cast<const float *>(src));
+ break;
+ case QShaderDescription::Vec2:
+ f->glUniform2fv(uniform.glslLocation, 1, reinterpret_cast<const float *>(src));
+ break;
+ case QShaderDescription::Vec3:
+ f->glUniform3fv(uniform.glslLocation, 1, reinterpret_cast<const float *>(src));
+ break;
+ case QShaderDescription::Vec4:
+ f->glUniform4fv(uniform.glslLocation, 1, reinterpret_cast<const float *>(src));
+ break;
+ case QShaderDescription::Mat2:
+ f->glUniformMatrix2fv(uniform.glslLocation, 1, GL_FALSE, reinterpret_cast<const float *>(src));
+ break;
+ case QShaderDescription::Mat3:
+ f->glUniformMatrix3fv(uniform.glslLocation, 1, GL_FALSE, reinterpret_cast<const float *>(src));
+ break;
+ case QShaderDescription::Mat4:
+ f->glUniformMatrix4fv(uniform.glslLocation, 1, GL_FALSE, reinterpret_cast<const float *>(src));
+ break;
+ case QShaderDescription::Int:
+ f->glUniform1i(uniform.glslLocation, *reinterpret_cast<const qint32 *>(src));
+ break;
+ case QShaderDescription::Int2:
+ f->glUniform2iv(uniform.glslLocation, 1, reinterpret_cast<const qint32 *>(src));
+ break;
+ case QShaderDescription::Int3:
+ f->glUniform3iv(uniform.glslLocation, 1, reinterpret_cast<const qint32 *>(src));
+ break;
+ case QShaderDescription::Int4:
+ f->glUniform4iv(uniform.glslLocation, 1, reinterpret_cast<const qint32 *>(src));
+ break;
+ // ### more types
+ default:
+ break;
+ }
+ }
+ }
+ }
+ break;
+ case QRhiShaderResourceBinding::SampledTexture:
+ {
+ QGles2Texture *texD = QRHI_RES(QGles2Texture, b->u.stex.tex);
+ QGles2Sampler *samplerD = QRHI_RES(QGles2Sampler, b->u.stex.sampler);
+
+ for (QGles2GraphicsPipeline::Sampler &sampler : psD->samplers) {
+ if (sampler.binding == b->binding) {
+ f->glActiveTexture(GL_TEXTURE0 + texUnit);
+ f->glBindTexture(texD->target, texD->texture);
+
+ if (texD->samplerState != samplerD->d) {
+ f->glTexParameteri(texD->target, GL_TEXTURE_MIN_FILTER, samplerD->d.glminfilter);
+ f->glTexParameteri(texD->target, GL_TEXTURE_MAG_FILTER, samplerD->d.glmagfilter);
+ f->glTexParameteri(texD->target, GL_TEXTURE_WRAP_S, samplerD->d.glwraps);
+ f->glTexParameteri(texD->target, GL_TEXTURE_WRAP_T, samplerD->d.glwrapt);
+ // 3D textures not supported by GLES 2.0 or by us atm...
+ //f->glTexParameteri(texD->target, GL_TEXTURE_WRAP_R, samplerD->d.glwrapr);
+ if (samplerD->d.gltexcomparefunc != GL_NEVER) {
+ f->glTexParameteri(texD->target, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
+ f->glTexParameteri(texD->target, GL_TEXTURE_COMPARE_FUNC, samplerD->d.gltexcomparefunc);
+ } else {
+ f->glTexParameteri(texD->target, GL_TEXTURE_COMPARE_MODE, GL_NONE);
+ }
+ texD->samplerState = samplerD->d;
+ }
+
+ f->glUniform1i(sampler.glslLocation, texUnit);
+ ++texUnit;
+ }
+ }
+ }
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ }
+
+ if (texUnit > 1)
+ f->glActiveTexture(GL_TEXTURE0);
+}
+
+void QRhiGles2::resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ Q_ASSERT(inFrame && !inPass);
+
+ enqueueResourceUpdates(cb, resourceUpdates);
+}
+
+QGles2RenderTargetData *QRhiGles2::enqueueBindFramebuffer(QRhiRenderTarget *rt, QGles2CommandBuffer *cbD,
+ bool *wantsColorClear, bool *wantsDsClear)
+{
+ QGles2RenderTargetData *rtD = nullptr;
+ QGles2CommandBuffer::Command fbCmd;
+ fbCmd.cmd = QGles2CommandBuffer::Command::BindFramebuffer;
+ switch (rt->resourceType()) {
+ case QRhiResource::RenderTarget:
+ rtD = &QRHI_RES(QGles2ReferenceRenderTarget, rt)->d;
+ if (wantsColorClear)
+ *wantsColorClear = true;
+ if (wantsDsClear)
+ *wantsDsClear = true;
+ fbCmd.args.bindFramebuffer.fbo = 0;
+ fbCmd.args.bindFramebuffer.colorAttCount = 1;
+ break;
+ case QRhiResource::TextureRenderTarget:
+ {
+ QGles2TextureRenderTarget *rtTex = QRHI_RES(QGles2TextureRenderTarget, rt);
+ rtD = &rtTex->d;
+ if (wantsColorClear)
+ *wantsColorClear = !rtTex->m_flags.testFlag(QRhiTextureRenderTarget::PreserveColorContents);
+ if (wantsDsClear)
+ *wantsDsClear = !rtTex->m_flags.testFlag(QRhiTextureRenderTarget::PreserveDepthStencilContents);
+ fbCmd.args.bindFramebuffer.fbo = rtTex->framebuffer;
+ fbCmd.args.bindFramebuffer.colorAttCount = rtD->colorAttCount;
+ }
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ fbCmd.args.bindFramebuffer.srgb = rtD->srgbUpdateAndBlend;
+ cbD->commands.append(fbCmd);
+ return rtD;
+}
+
+void QRhiGles2::beginPass(QRhiCommandBuffer *cb,
+ QRhiRenderTarget *rt,
+ const QColor &colorClearValue,
+ const QRhiDepthStencilClearValue &depthStencilClearValue,
+ QRhiResourceUpdateBatch *resourceUpdates)
+{
+ Q_ASSERT(inFrame && !inPass);
+
+ if (resourceUpdates)
+ enqueueResourceUpdates(cb, resourceUpdates);
+
+ QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
+ bool wantsColorClear, wantsDsClear;
+ QGles2RenderTargetData *rtD = enqueueBindFramebuffer(rt, cbD, &wantsColorClear, &wantsDsClear);
+
+ QGles2CommandBuffer::Command clearCmd;
+ clearCmd.cmd = QGles2CommandBuffer::Command::Clear;
+ clearCmd.args.clear.mask = 0;
+ if (rtD->colorAttCount && wantsColorClear)
+ clearCmd.args.clear.mask |= GL_COLOR_BUFFER_BIT;
+ if (rtD->dsAttCount && wantsDsClear)
+ clearCmd.args.clear.mask |= GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
+ clearCmd.args.clear.c[0] = colorClearValue.redF();
+ clearCmd.args.clear.c[1] = colorClearValue.greenF();
+ clearCmd.args.clear.c[2] = colorClearValue.blueF();
+ clearCmd.args.clear.c[3] = colorClearValue.alphaF();
+ clearCmd.args.clear.d = depthStencilClearValue.depthClearValue();
+ clearCmd.args.clear.s = depthStencilClearValue.stencilClearValue();
+ cbD->commands.append(clearCmd);
+
+ cbD->currentTarget = rt;
+
+ inPass = true;
+}
+
+void QRhiGles2::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ Q_ASSERT(inPass);
+ inPass = false;
+
+ QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
+ if (cbD->currentTarget->resourceType() == QRhiResource::TextureRenderTarget) {
+ QGles2TextureRenderTarget *rtTex = QRHI_RES(QGles2TextureRenderTarget, cbD->currentTarget);
+ const QVector<QRhiColorAttachment> colorAttachments = rtTex->m_desc.colorAttachments();
+ if (!colorAttachments.isEmpty()) {
+ // handle only 1 color attachment and only (msaa) renderbuffer
+ const QRhiColorAttachment &colorAtt(colorAttachments[0]);
+ if (colorAtt.resolveTexture()) {
+ Q_ASSERT(colorAtt.renderBuffer());
+ QGles2RenderBuffer *rbD = QRHI_RES(QGles2RenderBuffer, colorAtt.renderBuffer());
+ const QSize size = colorAtt.resolveTexture()->pixelSize();
+ if (rbD->pixelSize() != size) {
+ qWarning("Resolve source (%dx%d) and target (%dx%d) size does not match",
+ rbD->pixelSize().width(), rbD->pixelSize().height(), size.width(), size.height());
+ }
+ QGles2CommandBuffer::Command cmd;
+ cmd.cmd = QGles2CommandBuffer::Command::BlitFromRenderbuffer;
+ cmd.args.blitFromRb.renderbuffer = rbD->renderbuffer;
+ cmd.args.blitFromRb.w = size.width();
+ cmd.args.blitFromRb.h = size.height();
+ QGles2Texture *colorTexD = QRHI_RES(QGles2Texture, colorAtt.resolveTexture());
+ const GLenum faceTargetBase = colorTexD->m_flags.testFlag(QRhiTexture::CubeMap) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X
+ : colorTexD->target;
+ cmd.args.blitFromRb.target = faceTargetBase + colorAtt.resolveLayer();
+ cmd.args.blitFromRb.texture = colorTexD->texture;
+ cmd.args.blitFromRb.dstLevel = colorAtt.resolveLevel();
+ cbD->commands.append(cmd);
+ }
+ }
+ }
+
+ cbD->currentTarget = nullptr;
+
+ if (resourceUpdates)
+ enqueueResourceUpdates(cb, resourceUpdates);
+}
+
+QGles2Buffer::QGles2Buffer(QRhiImplementation *rhi, Type type, UsageFlags usage, int size)
+ : QRhiBuffer(rhi, type, usage, size)
+{
+}
+
+QGles2Buffer::~QGles2Buffer()
+{
+ release();
+}
+
+void QGles2Buffer::release()
+{
+ if (!buffer)
+ return;
+
+ QRhiGles2::DeferredReleaseEntry e;
+ e.type = QRhiGles2::DeferredReleaseEntry::Buffer;
+
+ e.buffer.buffer = buffer;
+
+ buffer = 0;
+
+ QRHI_RES_RHI(QRhiGles2);
+ rhiD->releaseQueue.append(e);
+ QRHI_PROF;
+ QRHI_PROF_F(releaseBuffer(this));
+ rhiD->unregisterResource(this);
+}
+
+bool QGles2Buffer::build()
+{
+ if (buffer)
+ release();
+
+ QRHI_RES_RHI(QRhiGles2);
+ QRHI_PROF;
+
+ if (m_usage.testFlag(QRhiBuffer::UniformBuffer)) {
+ if (m_usage.testFlag(QRhiBuffer::VertexBuffer) || m_usage.testFlag(QRhiBuffer::IndexBuffer)) {
+ qWarning("Uniform buffer: multiple usages specified, this is not supported by the OpenGL backend");
+ return false;
+ }
+ ubuf.resize(m_size);
+ QRHI_PROF_F(newBuffer(this, m_size, 0, 1));
+ return true;
+ }
+
+ if (!rhiD->ensureContext())
+ return false;
+
+ if (m_usage.testFlag(QRhiBuffer::VertexBuffer)) {
+ if (m_usage.testFlag(QRhiBuffer::IndexBuffer)) {
+ qWarning("Vertex buffer: multiple usages specified, this is not supported by the OpenGL backend");
+ return false;
+ }
+ target = GL_ARRAY_BUFFER;
+ }
+ if (m_usage.testFlag(QRhiBuffer::IndexBuffer))
+ target = GL_ELEMENT_ARRAY_BUFFER;
+
+ rhiD->f->glGenBuffers(1, &buffer);
+ rhiD->f->glBindBuffer(target, buffer);
+ rhiD->f->glBufferData(target, m_size, nullptr, m_type == Dynamic ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW);
+
+ QRHI_PROF_F(newBuffer(this, m_size, 1, 0));
+ rhiD->registerResource(this);
+ return true;
+}
+
+QGles2RenderBuffer::QGles2RenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize,
+ int sampleCount, QRhiRenderBuffer::Flags flags)
+ : QRhiRenderBuffer(rhi, type, pixelSize, sampleCount, flags)
+{
+}
+
+QGles2RenderBuffer::~QGles2RenderBuffer()
+{
+ release();
+}
+
+void QGles2RenderBuffer::release()
+{
+ if (!renderbuffer)
+ return;
+
+ QRhiGles2::DeferredReleaseEntry e;
+ e.type = QRhiGles2::DeferredReleaseEntry::RenderBuffer;
+
+ e.renderbuffer.renderbuffer = renderbuffer;
+ e.renderbuffer.renderbuffer2 = stencilRenderbuffer;
+
+ renderbuffer = 0;
+ stencilRenderbuffer = 0;
+
+ QRHI_RES_RHI(QRhiGles2);
+ rhiD->releaseQueue.append(e);
+ QRHI_PROF;
+ QRHI_PROF_F(releaseRenderBuffer(this));
+ rhiD->unregisterResource(this);
+}
+
+bool QGles2RenderBuffer::build()
+{
+ if (renderbuffer)
+ release();
+
+ QRHI_RES_RHI(QRhiGles2);
+ QRHI_PROF;
+ samples = rhiD->effectiveSampleCount(m_sampleCount);
+
+ if (m_flags.testFlag(UsedWithSwapChainOnly)) {
+ if (m_type == DepthStencil) {
+ QRHI_PROF_F(newRenderBuffer(this, false, true, samples));
+ return true;
+ }
+
+ qWarning("RenderBuffer: UsedWithSwapChainOnly is meaningless in combination with Color");
+ }
+
+ if (m_pixelSize.isEmpty())
+ return false;
+
+ if (!rhiD->ensureContext())
+ return false;
+
+ rhiD->f->glGenRenderbuffers(1, &renderbuffer);
+ rhiD->f->glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
+
+ switch (m_type) {
+ case QRhiRenderBuffer::DepthStencil:
+ if (rhiD->caps.msaaRenderBuffer && samples > 1) {
+ rhiD->f->glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, GL_DEPTH24_STENCIL8,
+ m_pixelSize.width(), m_pixelSize.height());
+ } else {
+ if (rhiD->caps.packedDepthStencil) {
+ rhiD->f->glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8,
+ m_pixelSize.width(), m_pixelSize.height());
+ stencilRenderbuffer = 0;
+ } else {
+ rhiD->f->glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_ATTACHMENT,
+ m_pixelSize.width(), m_pixelSize.height());
+ rhiD->f->glGenRenderbuffers(1, &stencilRenderbuffer);
+ rhiD->f->glBindRenderbuffer(GL_RENDERBUFFER, stencilRenderbuffer);
+ rhiD->f->glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_ATTACHMENT,
+ m_pixelSize.width(), m_pixelSize.height());
+ }
+ }
+ QRHI_PROF_F(newRenderBuffer(this, false, false, samples));
+ break;
+ case QRhiRenderBuffer::Color:
+ if (rhiD->caps.msaaRenderBuffer && samples > 1)
+ rhiD->f->glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, GL_RGBA8,
+ m_pixelSize.width(), m_pixelSize.height());
+ else
+ rhiD->f->glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8,
+ m_pixelSize.width(), m_pixelSize.height());
+ QRHI_PROF_F(newRenderBuffer(this, false, false, samples));
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+
+ rhiD->registerResource(this);
+ return true;
+}
+
+QRhiTexture::Format QGles2RenderBuffer::backingFormat() const
+{
+ return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat;
+}
+
+QGles2Texture::QGles2Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize,
+ int sampleCount, Flags flags)
+ : QRhiTexture(rhi, format, pixelSize, sampleCount, flags)
+{
+}
+
+QGles2Texture::~QGles2Texture()
+{
+ release();
+}
+
+void QGles2Texture::release()
+{
+ if (!texture)
+ return;
+
+ QRhiGles2::DeferredReleaseEntry e;
+ e.type = QRhiGles2::DeferredReleaseEntry::Texture;
+
+ e.texture.texture = texture;
+
+ texture = 0;
+ specified = false;
+ nativeHandlesStruct.texture = 0;
+
+ QRHI_RES_RHI(QRhiGles2);
+ if (owns)
+ rhiD->releaseQueue.append(e);
+ QRHI_PROF;
+ QRHI_PROF_F(releaseTexture(this));
+ rhiD->unregisterResource(this);
+}
+
+static inline bool isPowerOfTwo(int x)
+{
+ // Assumption: x >= 1
+ return x == (x & -x);
+}
+
+bool QGles2Texture::prepareBuild(QSize *adjustedSize)
+{
+ if (texture)
+ release();
+
+ QRHI_RES_RHI(QRhiGles2);
+ if (!rhiD->ensureContext())
+ return false;
+
+ QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize;
+ if (!rhiD->caps.npotTexture && (!isPowerOfTwo(size.width()) || !isPowerOfTwo(size.height())))
+ size = QSize(qNextPowerOfTwo(size.width()), qNextPowerOfTwo(size.height()));
+
+ const bool isCube = m_flags.testFlag(CubeMap);
+ const bool hasMipMaps = m_flags.testFlag(MipMapped);
+ const bool isCompressed = rhiD->isCompressedFormat(m_format);
+
+ target = isCube ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D;
+ mipLevelCount = hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1;
+ gltype = GL_UNSIGNED_BYTE;
+
+ if (isCompressed) {
+ glintformat = toGlCompressedTextureFormat(m_format, m_flags);
+ if (!glintformat) {
+ qWarning("Compressed format %d not mappable to GL compressed format", m_format);
+ return false;
+ }
+ glformat = GL_RGBA;
+ } else {
+ switch (m_format) {
+ case QRhiTexture::RGBA8:
+ glintformat = GL_RGBA;
+ glformat = GL_RGBA;
+ break;
+ case QRhiTexture::BGRA8:
+ glintformat = rhiD->caps.bgraInternalFormat ? GL_BGRA : GL_RGBA;
+ glformat = GL_BGRA;
+ break;
+ case QRhiTexture::R16:
+ glintformat = GL_R16;
+ glformat = GL_RED;
+ gltype = GL_UNSIGNED_SHORT;
+ break;
+ case QRhiTexture::R8:
+ glintformat = GL_R8;
+ glformat = GL_RED;
+ break;
+ case QRhiTexture::RED_OR_ALPHA8:
+ glintformat = rhiD->caps.coreProfile ? GL_R8 : GL_ALPHA;
+ glformat = rhiD->caps.coreProfile ? GL_RED : GL_ALPHA;
+ break;
+ case QRhiTexture::RGBA16F:
+ glintformat = GL_RGBA16F;
+ glformat = GL_RGBA;
+ gltype = GL_HALF_FLOAT;
+ break;
+ case QRhiTexture::RGBA32F:
+ glintformat = GL_RGBA32F;
+ glformat = GL_RGBA;
+ gltype = GL_FLOAT;
+ break;
+ case QRhiTexture::D16:
+ glintformat = GL_DEPTH_COMPONENT16;
+ glformat = GL_DEPTH_COMPONENT;
+ gltype = GL_UNSIGNED_SHORT;
+ break;
+ case QRhiTexture::D32F:
+ glintformat = GL_DEPTH_COMPONENT32F;
+ glformat = GL_DEPTH_COMPONENT;
+ gltype = GL_FLOAT;
+ break;
+ default:
+ Q_UNREACHABLE();
+ glintformat = GL_RGBA;
+ glformat = GL_RGBA;
+ break;
+ }
+ }
+
+ samplerState = QGles2SamplerData();
+
+ if (adjustedSize)
+ *adjustedSize = size;
+
+ return true;
+}
+
+bool QGles2Texture::build()
+{
+ QSize size;
+ if (!prepareBuild(&size))
+ return false;
+
+ QRHI_RES_RHI(QRhiGles2);
+ rhiD->f->glGenTextures(1, &texture);
+
+ const bool isCube = m_flags.testFlag(CubeMap);
+ const bool hasMipMaps = m_flags.testFlag(MipMapped);
+ const bool isCompressed = rhiD->isCompressedFormat(m_format);
+ if (!isCompressed) {
+ rhiD->f->glBindTexture(target, texture);
+ if (hasMipMaps || isCube) {
+ const GLenum faceTargetBase = isCube ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : target;
+ for (int layer = 0, layerCount = isCube ? 6 : 1; layer != layerCount; ++layer) {
+ for (int level = 0; level != mipLevelCount; ++level) {
+ const QSize mipSize = rhiD->q->sizeForMipLevel(level, size);
+ rhiD->f->glTexImage2D(faceTargetBase + layer, level, glintformat,
+ mipSize.width(), mipSize.height(), 0,
+ glformat, gltype, nullptr);
+ }
+ }
+ } else {
+ rhiD->f->glTexImage2D(target, 0, glintformat, size.width(), size.height(), 0, glformat, gltype, nullptr);
+ }
+ specified = true;
+ } else {
+ // Cannot use glCompressedTexImage2D without valid data, so defer.
+ // Compressed textures will not be used as render targets so this is
+ // not an issue.
+ specified = false;
+ }
+
+ QRHI_PROF;
+ QRHI_PROF_F(newTexture(this, true, mipLevelCount, isCube ? 6 : 1, 1));
+
+ owns = true;
+ nativeHandlesStruct.texture = texture;
+
+ generation += 1;
+ rhiD->registerResource(this);
+ return true;
+}
+
+bool QGles2Texture::buildFrom(const QRhiNativeHandles *src)
+{
+ const QRhiGles2TextureNativeHandles *h = static_cast<const QRhiGles2TextureNativeHandles *>(src);
+ if (!h || !h->texture)
+ return false;
+
+ if (!prepareBuild())
+ return false;
+
+ texture = h->texture;
+ specified = true;
+
+ QRHI_RES_RHI(QRhiGles2);
+ QRHI_PROF;
+ QRHI_PROF_F(newTexture(this, false, mipLevelCount, m_flags.testFlag(CubeMap) ? 6 : 1, 1));
+
+ owns = false;
+ nativeHandlesStruct.texture = texture;
+
+ generation += 1;
+ rhiD->registerResource(this);
+ return true;
+}
+
+const QRhiNativeHandles *QGles2Texture::nativeHandles()
+{
+ return &nativeHandlesStruct;
+}
+
+QGles2Sampler::QGles2Sampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode,
+ AddressMode u, AddressMode v)
+ : QRhiSampler(rhi, magFilter, minFilter, mipmapMode, u, v)
+{
+}
+
+QGles2Sampler::~QGles2Sampler()
+{
+ release();
+}
+
+void QGles2Sampler::release()
+{
+ // nothing to do here
+}
+
+bool QGles2Sampler::build()
+{
+ d.glminfilter = toGlMinFilter(m_minFilter, m_mipmapMode);
+ d.glmagfilter = toGlMagFilter(m_magFilter);
+ d.glwraps = toGlWrapMode(m_addressU);
+ d.glwrapt = toGlWrapMode(m_addressV);
+ d.glwrapr = toGlWrapMode(m_addressW);
+ d.gltexcomparefunc = toGlTextureCompareFunc(m_compareOp);
+
+ generation += 1;
+ return true;
+}
+
+// dummy, no Vulkan-style RenderPass+Framebuffer concept here
+QGles2RenderPassDescriptor::QGles2RenderPassDescriptor(QRhiImplementation *rhi)
+ : QRhiRenderPassDescriptor(rhi)
+{
+}
+
+QGles2RenderPassDescriptor::~QGles2RenderPassDescriptor()
+{
+ release();
+}
+
+void QGles2RenderPassDescriptor::release()
+{
+ // nothing to do here
+}
+
+QGles2ReferenceRenderTarget::QGles2ReferenceRenderTarget(QRhiImplementation *rhi)
+ : QRhiRenderTarget(rhi),
+ d(rhi)
+{
+}
+
+QGles2ReferenceRenderTarget::~QGles2ReferenceRenderTarget()
+{
+ release();
+}
+
+void QGles2ReferenceRenderTarget::release()
+{
+ // nothing to do here
+}
+
+QSize QGles2ReferenceRenderTarget::pixelSize() const
+{
+ return d.pixelSize;
+}
+
+float QGles2ReferenceRenderTarget::devicePixelRatio() const
+{
+ return d.dpr;
+}
+
+int QGles2ReferenceRenderTarget::sampleCount() const
+{
+ return d.sampleCount;
+}
+
+QGles2TextureRenderTarget::QGles2TextureRenderTarget(QRhiImplementation *rhi,
+ const QRhiTextureRenderTargetDescription &desc,
+ Flags flags)
+ : QRhiTextureRenderTarget(rhi, desc, flags),
+ d(rhi)
+{
+}
+
+QGles2TextureRenderTarget::~QGles2TextureRenderTarget()
+{
+ release();
+}
+
+void QGles2TextureRenderTarget::release()
+{
+ if (!framebuffer)
+ return;
+
+ QRhiGles2::DeferredReleaseEntry e;
+ e.type = QRhiGles2::DeferredReleaseEntry::TextureRenderTarget;
+
+ e.textureRenderTarget.framebuffer = framebuffer;
+
+ framebuffer = 0;
+
+ QRHI_RES_RHI(QRhiGles2);
+ rhiD->releaseQueue.append(e);
+
+ rhiD->unregisterResource(this);
+}
+
+QRhiRenderPassDescriptor *QGles2TextureRenderTarget::newCompatibleRenderPassDescriptor()
+{
+ return new QGles2RenderPassDescriptor(m_rhi);
+}
+
+bool QGles2TextureRenderTarget::build()
+{
+ QRHI_RES_RHI(QRhiGles2);
+
+ if (framebuffer)
+ release();
+
+ const QVector<QRhiColorAttachment> colorAttachments = m_desc.colorAttachments();
+ Q_ASSERT(!colorAttachments.isEmpty() || m_desc.depthTexture());
+ Q_ASSERT(!m_desc.depthStencilBuffer() || !m_desc.depthTexture());
+ const bool hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
+
+ if (colorAttachments.count() > rhiD->caps.maxDrawBuffers)
+ qWarning("QGles2TextureRenderTarget: Too many color attachments (%d, max is %d)",
+ colorAttachments.count(), rhiD->caps.maxDrawBuffers);
+ if (m_desc.depthTexture() && !rhiD->caps.depthTexture)
+ qWarning("QGles2TextureRenderTarget: Depth texture is not supported and will be ignored");
+
+ if (!rhiD->ensureContext())
+ return false;
+
+ rhiD->f->glGenFramebuffers(1, &framebuffer);
+ rhiD->f->glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
+
+ d.colorAttCount = colorAttachments.count();
+ for (int i = 0; i < d.colorAttCount; ++i) {
+ const QRhiColorAttachment &colorAtt(colorAttachments[i]);
+ QRhiTexture *texture = colorAtt.texture();
+ QRhiRenderBuffer *renderBuffer = colorAtt.renderBuffer();
+ Q_ASSERT(texture || renderBuffer);
+ if (texture) {
+ QGles2Texture *texD = QRHI_RES(QGles2Texture, texture);
+ Q_ASSERT(texD->texture && texD->specified);
+ const GLenum faceTargetBase = texD->flags().testFlag(QRhiTexture::CubeMap) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : texD->target;
+ rhiD->f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, faceTargetBase + colorAtt.layer(), texD->texture, colorAtt.level());
+ if (i == 0) {
+ d.pixelSize = texD->pixelSize();
+ d.sampleCount = 1;
+ }
+ } else if (renderBuffer) {
+ QGles2RenderBuffer *rbD = QRHI_RES(QGles2RenderBuffer, renderBuffer);
+ rhiD->f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_RENDERBUFFER, rbD->renderbuffer);
+ if (i == 0) {
+ d.pixelSize = rbD->pixelSize();
+ d.sampleCount = rbD->samples;
+ }
+ }
+ }
+
+ if (hasDepthStencil) {
+ if (m_desc.depthStencilBuffer()) {
+ QGles2RenderBuffer *depthRbD = QRHI_RES(QGles2RenderBuffer, m_desc.depthStencilBuffer());
+ rhiD->f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRbD->renderbuffer);
+ if (depthRbD->stencilRenderbuffer)
+ rhiD->f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depthRbD->stencilRenderbuffer);
+ else // packed
+ rhiD->f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depthRbD->renderbuffer);
+ if (d.colorAttCount == 0) {
+ d.pixelSize = depthRbD->pixelSize();
+ d.sampleCount = depthRbD->samples;
+ }
+ } else {
+ QGles2Texture *depthTexD = QRHI_RES(QGles2Texture, m_desc.depthTexture());
+ rhiD->f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTexD->texture, 0);
+ if (d.colorAttCount == 0) {
+ d.pixelSize = depthTexD->pixelSize();
+ d.sampleCount = 1;
+ }
+ }
+ d.dsAttCount = 1;
+ } else {
+ d.dsAttCount = 0;
+ }
+
+ d.dpr = 1;
+ d.rp = QRHI_RES(QGles2RenderPassDescriptor, m_renderPassDesc);
+
+ GLenum status = rhiD->f->glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ if (status != GL_NO_ERROR && status != GL_FRAMEBUFFER_COMPLETE) {
+ qWarning("Framebuffer incomplete: 0x%x", status);
+ return false;
+ }
+
+ rhiD->registerResource(this);
+ return true;
+}
+
+QSize QGles2TextureRenderTarget::pixelSize() const
+{
+ return d.pixelSize;
+}
+
+float QGles2TextureRenderTarget::devicePixelRatio() const
+{
+ return d.dpr;
+}
+
+int QGles2TextureRenderTarget::sampleCount() const
+{
+ return d.sampleCount;
+}
+
+QGles2ShaderResourceBindings::QGles2ShaderResourceBindings(QRhiImplementation *rhi)
+ : QRhiShaderResourceBindings(rhi)
+{
+}
+
+QGles2ShaderResourceBindings::~QGles2ShaderResourceBindings()
+{
+ release();
+}
+
+void QGles2ShaderResourceBindings::release()
+{
+ // nothing to do here
+}
+
+bool QGles2ShaderResourceBindings::build()
+{
+ generation += 1;
+ return true;
+}
+
+QGles2GraphicsPipeline::QGles2GraphicsPipeline(QRhiImplementation *rhi)
+ : QRhiGraphicsPipeline(rhi)
+{
+}
+
+QGles2GraphicsPipeline::~QGles2GraphicsPipeline()
+{
+ release();
+}
+
+void QGles2GraphicsPipeline::release()
+{
+ if (!program)
+ return;
+
+ QRhiGles2::DeferredReleaseEntry e;
+ e.type = QRhiGles2::DeferredReleaseEntry::Pipeline;
+
+ e.pipeline.program = program;
+
+ program = 0;
+ uniforms.clear();
+ samplers.clear();
+
+ QRHI_RES_RHI(QRhiGles2);
+ rhiD->releaseQueue.append(e);
+
+ rhiD->unregisterResource(this);
+}
+
+bool QGles2GraphicsPipeline::build()
+{
+ QRHI_RES_RHI(QRhiGles2);
+
+ if (program)
+ release();
+
+ if (!rhiD->ensureContext())
+ return false;
+
+ drawMode = toGlTopology(m_topology);
+
+ program = rhiD->f->glCreateProgram();
+
+ int sourceVer = 0;
+ for (const QRhiGraphicsShaderStage &shaderStage : qAsConst(m_shaderStages)) {
+ const bool isVertex = shaderStage.type() == QRhiGraphicsShaderStage::Vertex;
+ const bool isFragment = shaderStage.type() == QRhiGraphicsShaderStage::Fragment;
+ if (!isVertex && !isFragment)
+ continue;
+
+ GLuint shader = rhiD->f->glCreateShader(isVertex ? GL_VERTEX_SHADER : GL_FRAGMENT_SHADER);
+ const QShader bakedShader = shaderStage.shader();
+ QVector<int> versionsToTry;
+ QByteArray source;
+ if (rhiD->caps.gles) {
+ if (rhiD->caps.ctxMajor > 3 || (rhiD->caps.ctxMajor == 3 && rhiD->caps.ctxMinor >= 2)) {
+ versionsToTry << 320 << 310 << 300 << 100;
+ } else if (rhiD->caps.ctxMajor == 3 && rhiD->caps.ctxMinor == 1) {
+ versionsToTry << 310 << 300 << 100;
+ } else if (rhiD->caps.ctxMajor == 3 && rhiD->caps.ctxMinor == 0) {
+ versionsToTry << 300 << 100;
+ } else {
+ versionsToTry << 100;
+ }
+ for (int v : versionsToTry) {
+ QShaderVersion ver(v, QShaderVersion::GlslEs);
+ source = bakedShader.shader({ QShader::GlslShader, ver, shaderStage.shaderVariant() }).shader();
+ if (!source.isEmpty()) {
+ sourceVer = v;
+ break;
+ }
+ }
+ } else {
+ if (rhiD->caps.ctxMajor > 4 || (rhiD->caps.ctxMajor == 4 && rhiD->caps.ctxMinor >= 6)) {
+ versionsToTry << 460 << 450 << 440 << 430 << 420 << 410 << 400 << 330 << 150;
+ } else if (rhiD->caps.ctxMajor == 4 && rhiD->caps.ctxMinor == 5) {
+ versionsToTry << 450 << 440 << 430 << 420 << 410 << 400 << 330 << 150;
+ } else if (rhiD->caps.ctxMajor == 4 && rhiD->caps.ctxMinor == 4) {
+ versionsToTry << 440 << 430 << 420 << 410 << 400 << 330 << 150;
+ } else if (rhiD->caps.ctxMajor == 4 && rhiD->caps.ctxMinor == 3) {
+ versionsToTry << 430 << 420 << 410 << 400 << 330 << 150;
+ } else if (rhiD->caps.ctxMajor == 4 && rhiD->caps.ctxMinor == 2) {
+ versionsToTry << 420 << 410 << 400 << 330 << 150;
+ } else if (rhiD->caps.ctxMajor == 4 && rhiD->caps.ctxMinor == 1) {
+ versionsToTry << 410 << 400 << 330 << 150;
+ } else if (rhiD->caps.ctxMajor == 4 && rhiD->caps.ctxMinor == 0) {
+ versionsToTry << 400 << 330 << 150;
+ } else if (rhiD->caps.ctxMajor == 3 && rhiD->caps.ctxMinor == 3) {
+ versionsToTry << 330 << 150;
+ } else if (rhiD->caps.ctxMajor == 3 && rhiD->caps.ctxMinor == 2) {
+ versionsToTry << 150;
+ }
+ if (!rhiD->caps.coreProfile)
+ versionsToTry << 120;
+ for (int v : versionsToTry) {
+ source = bakedShader.shader({ QShader::GlslShader, v, shaderStage.shaderVariant() }).shader();
+ if (!source.isEmpty()) {
+ sourceVer = v;
+ break;
+ }
+ }
+ }
+ if (source.isEmpty()) {
+ qWarning() << "No GLSL shader code found (versions tried: " << versionsToTry
+ << ") in baked shader" << bakedShader;
+ return false;
+ }
+
+ const char *srcStr = source.constData();
+ const GLint srcLength = source.count();
+ rhiD->f->glShaderSource(shader, 1, &srcStr, &srcLength);
+ rhiD->f->glCompileShader(shader);
+ GLint compiled = 0;
+ rhiD->f->glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
+ if (!compiled) {
+ GLint infoLogLength = 0;
+ rhiD->f->glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLength);
+ QByteArray log;
+ if (infoLogLength > 1) {
+ GLsizei length = 0;
+ log.resize(infoLogLength);
+ rhiD->f->glGetShaderInfoLog(shader, infoLogLength, &length, log.data());
+ }
+ qWarning("Failed to compile shader: %s\nSource was:\n%s", log.constData(), source.constData());
+ return false;
+ }
+
+ rhiD->f->glAttachShader(program, shader);
+ rhiD->f->glDeleteShader(shader);
+
+ if (isVertex)
+ vsDesc = bakedShader.description();
+ else
+ fsDesc = bakedShader.description();
+ }
+
+ // not very useful atm since we assume the qsb-generated GLSL shaders never use uniform blocks
+ canUseUniformBuffers = rhiD->caps.uniformBuffers && sourceVer >= 140;
+
+ for (auto inVar : vsDesc.inputVariables()) {
+ const QByteArray name = inVar.name.toUtf8();
+ rhiD->f->glBindAttribLocation(program, inVar.location, name.constData());
+ }
+
+ rhiD->f->glLinkProgram(program);
+ GLint linked = 0;
+ rhiD->f->glGetProgramiv(program, GL_LINK_STATUS, &linked);
+ if (!linked) {
+ GLint infoLogLength = 0;
+ rhiD->f->glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLogLength);
+ QByteArray log;
+ if (infoLogLength > 1) {
+ GLsizei length = 0;
+ log.resize(infoLogLength);
+ rhiD->f->glGetProgramInfoLog(program, infoLogLength, &length, log.data());
+ }
+ qWarning("Failed to link shader program: %s", log.constData());
+ return false;
+ }
+
+ auto lookupUniforms = [this, rhiD](const QShaderDescription::UniformBlock &ub) {
+ const QByteArray prefix = ub.structName.toUtf8() + '.';
+ for (const QShaderDescription::BlockVariable &blockMember : ub.members) {
+ // ### no array support for now
+ Uniform uniform;
+ uniform.type = blockMember.type;
+ const QByteArray name = prefix + blockMember.name.toUtf8();
+ uniform.glslLocation = rhiD->f->glGetUniformLocation(program, name.constData());
+ if (uniform.glslLocation >= 0) {
+ uniform.binding = ub.binding;
+ uniform.offset = blockMember.offset;
+ uniform.size = blockMember.size;
+ uniforms.append(uniform);
+ }
+ }
+ };
+
+ for (const QShaderDescription::UniformBlock &ub : vsDesc.uniformBlocks())
+ lookupUniforms(ub);
+
+ for (const QShaderDescription::UniformBlock &ub : fsDesc.uniformBlocks())
+ lookupUniforms(ub);
+
+ auto lookupSamplers = [this, rhiD](const QShaderDescription::InOutVariable &v) {
+ Sampler sampler;
+ const QByteArray name = v.name.toUtf8();
+ sampler.glslLocation = rhiD->f->glGetUniformLocation(program, name.constData());
+ if (sampler.glslLocation >= 0) {
+ sampler.binding = v.binding;
+ samplers.append(sampler);
+ }
+ };
+
+ for (const QShaderDescription::InOutVariable &v : vsDesc.combinedImageSamplers())
+ lookupSamplers(v);
+
+ for (const QShaderDescription::InOutVariable &v : fsDesc.combinedImageSamplers())
+ lookupSamplers(v);
+
+ generation += 1;
+ rhiD->registerResource(this);
+ return true;
+}
+
+QGles2CommandBuffer::QGles2CommandBuffer(QRhiImplementation *rhi)
+ : QRhiCommandBuffer(rhi)
+{
+ resetState();
+}
+
+QGles2CommandBuffer::~QGles2CommandBuffer()
+{
+ release();
+}
+
+void QGles2CommandBuffer::release()
+{
+ // nothing to do here
+}
+
+QGles2SwapChain::QGles2SwapChain(QRhiImplementation *rhi)
+ : QRhiSwapChain(rhi),
+ rt(rhi),
+ cb(rhi)
+{
+}
+
+QGles2SwapChain::~QGles2SwapChain()
+{
+ release();
+}
+
+void QGles2SwapChain::release()
+{
+ QRHI_PROF;
+ QRHI_PROF_F(releaseSwapChain(this));
+}
+
+QRhiCommandBuffer *QGles2SwapChain::currentFrameCommandBuffer()
+{
+ return &cb;
+}
+
+QRhiRenderTarget *QGles2SwapChain::currentFrameRenderTarget()
+{
+ return &rt;
+}
+
+QSize QGles2SwapChain::surfacePixelSize()
+{
+ Q_ASSERT(m_window);
+ return m_window->size() * m_window->devicePixelRatio();
+}
+
+QRhiRenderPassDescriptor *QGles2SwapChain::newCompatibleRenderPassDescriptor()
+{
+ return new QGles2RenderPassDescriptor(m_rhi);
+}
+
+bool QGles2SwapChain::buildOrResize()
+{
+ surface = m_window;
+ m_currentPixelSize = surfacePixelSize();
+ pixelSize = m_currentPixelSize;
+
+ rt.d.rp = QRHI_RES(QGles2RenderPassDescriptor, m_renderPassDesc);
+ rt.d.pixelSize = pixelSize;
+ rt.d.dpr = m_window->devicePixelRatio();
+ rt.d.sampleCount = qBound(1, m_sampleCount, 64);
+ rt.d.colorAttCount = 1;
+ rt.d.dsAttCount = m_depthStencil ? 1 : 0;
+ rt.d.srgbUpdateAndBlend = m_flags.testFlag(QRhiSwapChain::sRGB);
+
+ frameCount = 0;
+
+ QRHI_PROF;
+ // make something up
+ QRHI_PROF_F(resizeSwapChain(this, 2, m_sampleCount > 1 ? 2 : 0, m_sampleCount));
+
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/rhi/qrhigles2_p.h b/src/gui/rhi/qrhigles2_p.h
new file mode 100644
index 0000000000..7f7c8b4c40
--- /dev/null
+++ b/src/gui/rhi/qrhigles2_p.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gui module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QRHIGLES2_H
+#define QRHIGLES2_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qrhi_p.h>
+#include <QtGui/qsurfaceformat.h>
+
+QT_BEGIN_NAMESPACE
+
+class QOpenGLContext;
+class QOffscreenSurface;
+class QWindow;
+
+struct Q_GUI_EXPORT QRhiGles2InitParams : public QRhiInitParams
+{
+ QRhiGles2InitParams();
+
+ QSurfaceFormat format;
+ QOffscreenSurface *fallbackSurface = nullptr;
+ QWindow *window = nullptr;
+
+ static QOffscreenSurface *newFallbackSurface(const QSurfaceFormat &format = QSurfaceFormat::defaultFormat());
+ static QSurfaceFormat adjustedFormat(const QSurfaceFormat &format = QSurfaceFormat::defaultFormat());
+};
+
+struct Q_GUI_EXPORT QRhiGles2NativeHandles : public QRhiNativeHandles
+{
+ QOpenGLContext *context = nullptr;
+};
+
+struct Q_GUI_EXPORT QRhiGles2TextureNativeHandles : public QRhiNativeHandles
+{
+ uint texture = 0;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/rhi/qrhigles2_p_p.h b/src/gui/rhi/qrhigles2_p_p.h
new file mode 100644
index 0000000000..d1a8a1fadf
--- /dev/null
+++ b/src/gui/rhi/qrhigles2_p_p.h
@@ -0,0 +1,695 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gui module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QRHIGLES2_P_H
+#define QRHIGLES2_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qrhigles2_p.h"
+#include "qrhi_p_p.h"
+#include "qshaderdescription_p.h"
+#include <qopengl.h>
+#include <QSurface>
+
+QT_BEGIN_NAMESPACE
+
+class QOpenGLExtensions;
+
+struct QGles2Buffer : public QRhiBuffer
+{
+ QGles2Buffer(QRhiImplementation *rhi, Type type, UsageFlags usage, int size);
+ ~QGles2Buffer();
+ void release() override;
+ bool build() override;
+
+ GLuint buffer = 0;
+ GLenum target;
+ QByteArray ubuf;
+ friend class QRhiGles2;
+};
+
+struct QGles2RenderBuffer : public QRhiRenderBuffer
+{
+ QGles2RenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize,
+ int sampleCount, QRhiRenderBuffer::Flags flags);
+ ~QGles2RenderBuffer();
+ void release() override;
+ bool build() override;
+ QRhiTexture::Format backingFormat() const override;
+
+ GLuint renderbuffer = 0;
+ GLuint stencilRenderbuffer = 0; // when packed depth-stencil not supported
+ int samples;
+ friend class QRhiGles2;
+};
+
+struct QGles2SamplerData
+{
+ GLenum glminfilter = 0;
+ GLenum glmagfilter = 0;
+ GLenum glwraps = 0;
+ GLenum glwrapt = 0;
+ GLenum glwrapr = 0;
+ GLenum gltexcomparefunc = 0;
+};
+
+inline bool operator==(const QGles2SamplerData &a, const QGles2SamplerData &b)
+{
+ return a.glminfilter == b.glminfilter
+ && a.glmagfilter == b.glmagfilter
+ && a.glwraps == b.glwraps
+ && a.glwrapt == b.glwrapt
+ && a.glwrapr == b.glwrapr
+ && a.gltexcomparefunc == b.gltexcomparefunc;
+}
+
+inline bool operator!=(const QGles2SamplerData &a, const QGles2SamplerData &b)
+{
+ return !(a == b);
+}
+
+struct QGles2Texture : public QRhiTexture
+{
+ QGles2Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize,
+ int sampleCount, Flags flags);
+ ~QGles2Texture();
+ void release() override;
+ bool build() override;
+ bool buildFrom(const QRhiNativeHandles *src) override;
+ const QRhiNativeHandles *nativeHandles() override;
+
+ bool prepareBuild(QSize *adjustedSize = nullptr);
+
+ GLuint texture = 0;
+ bool owns = true;
+ GLenum target;
+ GLenum glintformat;
+ GLenum glformat;
+ GLenum gltype;
+ QGles2SamplerData samplerState;
+ bool specified = false;
+ int mipLevelCount = 0;
+ QRhiGles2TextureNativeHandles nativeHandlesStruct;
+
+ uint generation = 0;
+ friend class QRhiGles2;
+};
+
+struct QGles2Sampler : public QRhiSampler
+{
+ QGles2Sampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode,
+ AddressMode u, AddressMode v);
+ ~QGles2Sampler();
+ void release() override;
+ bool build() override;
+
+ QGles2SamplerData d;
+ uint generation = 0;
+ friend class QRhiGles2;
+};
+
+struct QGles2RenderPassDescriptor : public QRhiRenderPassDescriptor
+{
+ QGles2RenderPassDescriptor(QRhiImplementation *rhi);
+ ~QGles2RenderPassDescriptor();
+ void release() override;
+};
+
+struct QGles2RenderTargetData
+{
+ QGles2RenderTargetData(QRhiImplementation *) { }
+
+ QGles2RenderPassDescriptor *rp = nullptr;
+ QSize pixelSize;
+ float dpr = 1;
+ int sampleCount = 1;
+ int colorAttCount = 0;
+ int dsAttCount = 0;
+ bool srgbUpdateAndBlend = false;
+};
+
+struct QGles2ReferenceRenderTarget : public QRhiRenderTarget
+{
+ QGles2ReferenceRenderTarget(QRhiImplementation *rhi);
+ ~QGles2ReferenceRenderTarget();
+ void release() override;
+
+ QSize pixelSize() const override;
+ float devicePixelRatio() const override;
+ int sampleCount() const override;
+
+ QGles2RenderTargetData d;
+};
+
+struct QGles2TextureRenderTarget : public QRhiTextureRenderTarget
+{
+ QGles2TextureRenderTarget(QRhiImplementation *rhi, const QRhiTextureRenderTargetDescription &desc, Flags flags);
+ ~QGles2TextureRenderTarget();
+ void release() override;
+
+ QSize pixelSize() const override;
+ float devicePixelRatio() const override;
+ int sampleCount() const override;
+
+ QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override;
+ bool build() override;
+
+ QGles2RenderTargetData d;
+ GLuint framebuffer = 0;
+ friend class QRhiGles2;
+};
+
+struct QGles2ShaderResourceBindings : public QRhiShaderResourceBindings
+{
+ QGles2ShaderResourceBindings(QRhiImplementation *rhi);
+ ~QGles2ShaderResourceBindings();
+ void release() override;
+ bool build() override;
+
+ uint generation = 0;
+ friend class QRhiGles2;
+};
+
+struct QGles2GraphicsPipeline : public QRhiGraphicsPipeline
+{
+ QGles2GraphicsPipeline(QRhiImplementation *rhi);
+ ~QGles2GraphicsPipeline();
+ void release() override;
+ bool build() override;
+
+ GLuint program = 0;
+ GLenum drawMode = GL_TRIANGLES;
+ QShaderDescription vsDesc;
+ QShaderDescription fsDesc;
+ bool canUseUniformBuffers = false;
+
+ struct Uniform {
+ QShaderDescription::VariableType type;
+ int glslLocation;
+ int binding;
+ uint offset;
+ int size;
+ };
+ QVector<Uniform> uniforms;
+
+ struct Sampler {
+ int glslLocation;
+ int binding;
+ };
+ QVector<Sampler> samplers;
+
+ uint generation = 0;
+ friend class QRhiGles2;
+};
+
+Q_DECLARE_TYPEINFO(QGles2GraphicsPipeline::Uniform, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(QGles2GraphicsPipeline::Sampler, Q_MOVABLE_TYPE);
+
+struct QGles2CommandBuffer : public QRhiCommandBuffer
+{
+ QGles2CommandBuffer(QRhiImplementation *rhi);
+ ~QGles2CommandBuffer();
+ void release() override;
+
+ struct Command {
+ enum Cmd {
+ BeginFrame,
+ EndFrame,
+ Viewport,
+ Scissor,
+ BlendConstants,
+ StencilRef,
+ BindVertexBuffer,
+ BindIndexBuffer,
+ Draw,
+ DrawIndexed,
+ BindGraphicsPipeline,
+ BindShaderResources,
+ BindFramebuffer,
+ Clear,
+ BufferData,
+ BufferSubData,
+ CopyTex,
+ ReadPixels,
+ SubImage,
+ CompressedImage,
+ CompressedSubImage,
+ BlitFromRenderbuffer,
+ GenMip
+ };
+ Cmd cmd;
+
+ static const int MAX_UBUF_BINDINGS = 32; // should be more than enough
+
+ // QRhi*/QGles2* references should be kept at minimum (so no
+ // QRhiTexture/Buffer/etc. pointers).
+ union {
+ struct {
+ float x, y, w, h;
+ float d0, d1;
+ } viewport;
+ struct {
+ int x, y, w, h;
+ } scissor;
+ struct {
+ float r, g, b, a;
+ } blendConstants;
+ struct {
+ quint32 ref;
+ QRhiGraphicsPipeline *ps;
+ } stencilRef;
+ struct {
+ QRhiGraphicsPipeline *ps;
+ GLuint buffer;
+ quint32 offset;
+ int binding;
+ } bindVertexBuffer;
+ struct {
+ GLuint buffer;
+ quint32 offset;
+ GLenum type;
+ } bindIndexBuffer;
+ struct {
+ QRhiGraphicsPipeline *ps;
+ quint32 vertexCount;
+ quint32 firstVertex;
+ } draw;
+ struct {
+ QRhiGraphicsPipeline *ps;
+ quint32 indexCount;
+ quint32 firstIndex;
+ } drawIndexed;
+ struct {
+ QRhiGraphicsPipeline *ps;
+ } bindGraphicsPipeline;
+ struct {
+ QRhiGraphicsPipeline *ps;
+ QRhiShaderResourceBindings *srb;
+ int dynamicOffsetCount;
+ uint dynamicOffsetPairs[MAX_UBUF_BINDINGS * 2]; // binding, offsetInConstants
+ } bindShaderResources;
+ struct {
+ GLbitfield mask;
+ float c[4];
+ float d;
+ quint32 s;
+ } clear;
+ struct {
+ GLuint fbo;
+ bool srgb;
+ int colorAttCount;
+ } bindFramebuffer;
+ struct {
+ GLenum target;
+ GLuint buffer;
+ int offset;
+ int size;
+ const void *data; // must come from retainData()
+ } bufferSubData;
+ struct {
+ GLenum srcFaceTarget;
+ GLuint srcTexture;
+ int srcLevel;
+ int srcX;
+ int srcY;
+ GLenum dstTarget;
+ GLuint dstTexture;
+ GLenum dstFaceTarget;
+ int dstLevel;
+ int dstX;
+ int dstY;
+ int w;
+ int h;
+ } copyTex;
+ struct {
+ QRhiReadbackResult *result;
+ GLuint texture;
+ int w;
+ int h;
+ QRhiTexture::Format format;
+ GLenum readTarget;
+ int level;
+ } readPixels;
+ struct {
+ GLenum target;
+ GLuint texture;
+ GLenum faceTarget;
+ int level;
+ int dx;
+ int dy;
+ int w;
+ int h;
+ GLenum glformat;
+ GLenum gltype;
+ int rowStartAlign;
+ const void *data; // must come from retainImage()
+ } subImage;
+ struct {
+ GLenum target;
+ GLuint texture;
+ GLenum faceTarget;
+ int level;
+ GLenum glintformat;
+ int w;
+ int h;
+ int size;
+ const void *data; // must come from retainData()
+ } compressedImage;
+ struct {
+ GLenum target;
+ GLuint texture;
+ GLenum faceTarget;
+ int level;
+ int dx;
+ int dy;
+ int w;
+ int h;
+ GLenum glintformat;
+ int size;
+ const void *data; // must come from retainData()
+ } compressedSubImage;
+ struct {
+ GLuint renderbuffer;
+ int w;
+ int h;
+ GLenum target;
+ GLuint texture;
+ int dstLevel;
+ } blitFromRb;
+ struct {
+ GLenum target;
+ GLuint texture;
+ } genMip;
+ } args;
+ };
+
+ QVector<Command> commands;
+ QRhiRenderTarget *currentTarget;
+ QRhiGraphicsPipeline *currentPipeline;
+ uint currentPipelineGeneration;
+ QRhiShaderResourceBindings *currentSrb;
+ uint currentSrbGeneration;
+
+ QVector<QByteArray> dataRetainPool;
+ QVector<QImage> imageRetainPool;
+
+ // relies heavily on implicit sharing (no copies of the actual data will be made)
+ const void *retainData(const QByteArray &data) {
+ dataRetainPool.append(data);
+ return dataRetainPool.constLast().constData();
+ }
+ const void *retainImage(const QImage &image) {
+ imageRetainPool.append(image);
+ return imageRetainPool.constLast().constBits();
+ }
+ void resetCommands() {
+ commands.clear();
+ dataRetainPool.clear();
+ imageRetainPool.clear();
+ }
+ void resetState() {
+ resetCommands();
+ currentTarget = nullptr;
+ resetCachedState();
+ }
+ void resetCachedState() {
+ currentPipeline = nullptr;
+ currentPipelineGeneration = 0;
+ currentSrb = nullptr;
+ currentSrbGeneration = 0;
+ }
+};
+
+Q_DECLARE_TYPEINFO(QGles2CommandBuffer::Command, Q_MOVABLE_TYPE);
+
+struct QGles2SwapChain : public QRhiSwapChain
+{
+ QGles2SwapChain(QRhiImplementation *rhi);
+ ~QGles2SwapChain();
+ void release() override;
+
+ QRhiCommandBuffer *currentFrameCommandBuffer() override;
+ QRhiRenderTarget *currentFrameRenderTarget() override;
+
+ QSize surfacePixelSize() override;
+
+ QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override;
+ bool buildOrResize() override;
+
+ QSurface *surface = nullptr;
+ QSize pixelSize;
+ QGles2ReferenceRenderTarget rt;
+ QGles2CommandBuffer cb;
+ int frameCount = 0;
+};
+
+class QRhiGles2 : public QRhiImplementation
+{
+public:
+ QRhiGles2(QRhiGles2InitParams *params, QRhiGles2NativeHandles *importDevice = nullptr);
+
+ bool create(QRhi::Flags flags) override;
+ void destroy() override;
+
+ QRhiGraphicsPipeline *createGraphicsPipeline() override;
+ QRhiShaderResourceBindings *createShaderResourceBindings() override;
+ QRhiBuffer *createBuffer(QRhiBuffer::Type type,
+ QRhiBuffer::UsageFlags usage,
+ int size) override;
+ QRhiRenderBuffer *createRenderBuffer(QRhiRenderBuffer::Type type,
+ const QSize &pixelSize,
+ int sampleCount,
+ QRhiRenderBuffer::Flags flags) override;
+ QRhiTexture *createTexture(QRhiTexture::Format format,
+ const QSize &pixelSize,
+ int sampleCount,
+ QRhiTexture::Flags flags) override;
+ QRhiSampler *createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
+ QRhiSampler::Filter mipmapMode,
+ QRhiSampler:: AddressMode u, QRhiSampler::AddressMode v) override;
+
+ QRhiTextureRenderTarget *createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
+ QRhiTextureRenderTarget::Flags flags) override;
+
+ QRhiSwapChain *createSwapChain() override;
+ QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override;
+ QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) override;
+ QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb) override;
+ QRhi::FrameOpResult endOffscreenFrame() override;
+ QRhi::FrameOpResult finish() override;
+
+ void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;
+
+ void beginPass(QRhiCommandBuffer *cb,
+ QRhiRenderTarget *rt,
+ const QColor &colorClearValue,
+ const QRhiDepthStencilClearValue &depthStencilClearValue,
+ QRhiResourceUpdateBatch *resourceUpdates) override;
+ void endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;
+
+ void setGraphicsPipeline(QRhiCommandBuffer *cb,
+ QRhiGraphicsPipeline *ps) override;
+
+ void setShaderResources(QRhiCommandBuffer *cb,
+ QRhiShaderResourceBindings *srb,
+ int dynamicOffsetCount,
+ const QRhiCommandBuffer::DynamicOffset *dynamicOffsets) override;
+
+ void setVertexInput(QRhiCommandBuffer *cb,
+ int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings,
+ QRhiBuffer *indexBuf, quint32 indexOffset,
+ QRhiCommandBuffer::IndexFormat indexFormat) override;
+
+ void setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport) override;
+ void setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) override;
+ void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) override;
+ void setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) override;
+
+ void draw(QRhiCommandBuffer *cb, quint32 vertexCount,
+ quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) override;
+
+ void drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
+ quint32 instanceCount, quint32 firstIndex,
+ qint32 vertexOffset, quint32 firstInstance) override;
+
+ void debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name) override;
+ void debugMarkEnd(QRhiCommandBuffer *cb) override;
+ void debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg) override;
+
+ const QRhiNativeHandles *nativeHandles(QRhiCommandBuffer *cb) override;
+ void beginExternal(QRhiCommandBuffer *cb) override;
+ void endExternal(QRhiCommandBuffer *cb) override;
+
+ QVector<int> supportedSampleCounts() const override;
+ int ubufAlignment() const override;
+ bool isYUpInFramebuffer() const override;
+ bool isYUpInNDC() const override;
+ bool isClipDepthZeroToOne() const override;
+ QMatrix4x4 clipSpaceCorrMatrix() const override;
+ bool isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const override;
+ bool isFeatureSupported(QRhi::Feature feature) const override;
+ int resourceLimit(QRhi::ResourceLimit limit) const override;
+ const QRhiNativeHandles *nativeHandles() override;
+ void sendVMemStatsToProfiler() override;
+
+ bool ensureContext(QSurface *surface = nullptr) const;
+ void executeDeferredReleases();
+ QRhi::FrameOpResult flushCommandBuffer();
+ void enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cbD,
+ int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc);
+ void enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates);
+ void executeCommandBuffer(QRhiCommandBuffer *cb);
+ void executeBindGraphicsPipeline(QRhiGraphicsPipeline *ps);
+ void bindShaderResources(QRhiGraphicsPipeline *ps, QRhiShaderResourceBindings *srb,
+ const uint *dynOfsPairs, int dynOfsCount);
+ QGles2RenderTargetData *enqueueBindFramebuffer(QRhiRenderTarget *rt, QGles2CommandBuffer *cbD,
+ bool *wantsColorClear = nullptr, bool *wantsDsClear = nullptr);
+ int effectiveSampleCount(int sampleCount) const;
+
+ QOpenGLContext *ctx = nullptr;
+ bool importedContext = false;
+ QSurfaceFormat requestedFormat;
+ QSurface *fallbackSurface = nullptr;
+ QWindow *maybeWindow = nullptr;
+ mutable bool needsMakeCurrent = false;
+ QOpenGLExtensions *f = nullptr;
+ uint vao = 0;
+ struct Caps {
+ Caps()
+ : ctxMajor(2),
+ ctxMinor(0),
+ maxTextureSize(2048),
+ maxDrawBuffers(4),
+ msaaRenderBuffer(false),
+ npotTexture(true),
+ npotTextureRepeat(true),
+ gles(false),
+ fixedIndexPrimitiveRestart(false),
+ bgraExternalFormat(false),
+ bgraInternalFormat(false),
+ r8Format(false),
+ r16Format(false),
+ floatFormats(false),
+ depthTexture(false),
+ packedDepthStencil(false),
+ srgbCapableDefaultFramebuffer(false),
+ coreProfile(false),
+ uniformBuffers(false),
+ elementIndexUint(false)
+ { }
+ int ctxMajor;
+ int ctxMinor;
+ int maxTextureSize;
+ int maxDrawBuffers;
+ int maxSamples;
+ // Multisample fb and blit are supported (GLES 3.0 or OpenGL 3.x). Not
+ // the same as multisample textures!
+ uint msaaRenderBuffer : 1;
+ uint npotTexture : 1;
+ uint npotTextureRepeat : 1;
+ uint gles : 1;
+ uint fixedIndexPrimitiveRestart : 1;
+ uint bgraExternalFormat : 1;
+ uint bgraInternalFormat : 1;
+ uint r8Format : 1;
+ uint r16Format : 1;
+ uint floatFormats : 1;
+ uint depthTexture : 1;
+ uint packedDepthStencil : 1;
+ uint srgbCapableDefaultFramebuffer : 1;
+ uint coreProfile : 1;
+ uint uniformBuffers : 1;
+ uint elementIndexUint : 1;
+ } caps;
+ bool inFrame = false;
+ bool inPass = false;
+ QGles2SwapChain *currentSwapChain = nullptr;
+ QVector<GLint> supportedCompressedFormats;
+ mutable QVector<int> supportedSampleCountList;
+ QRhiGles2NativeHandles nativeHandlesStruct;
+
+ struct DeferredReleaseEntry {
+ enum Type {
+ Buffer,
+ Pipeline,
+ Texture,
+ RenderBuffer,
+ TextureRenderTarget
+ };
+ Type type;
+ union {
+ struct {
+ GLuint buffer;
+ } buffer;
+ struct {
+ GLuint program;
+ } pipeline;
+ struct {
+ GLuint texture;
+ } texture;
+ struct {
+ GLuint renderbuffer;
+ GLuint renderbuffer2;
+ } renderbuffer;
+ struct {
+ GLuint framebuffer;
+ } textureRenderTarget;
+ };
+ };
+ QVector<DeferredReleaseEntry> releaseQueue;
+
+ struct OffscreenFrame {
+ OffscreenFrame(QRhiImplementation *rhi) : cbWrapper(rhi) { }
+ bool active = false;
+ QGles2CommandBuffer cbWrapper;
+ } ofr;
+};
+
+Q_DECLARE_TYPEINFO(QRhiGles2::DeferredReleaseEntry, Q_MOVABLE_TYPE);
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/rhi/qrhimetal.mm b/src/gui/rhi/qrhimetal.mm
new file mode 100644
index 0000000000..8739a42738
--- /dev/null
+++ b/src/gui/rhi/qrhimetal.mm
@@ -0,0 +1,3249 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gui module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qrhimetal_p_p.h"
+#include "qshader_p.h"
+#include <QGuiApplication>
+#include <QWindow>
+#include <qmath.h>
+
+#ifdef Q_OS_MACOS
+#include <AppKit/AppKit.h>
+#endif
+
+#include <Metal/Metal.h>
+#include <QuartzCore/CAMetalLayer.h>
+
+QT_BEGIN_NAMESPACE
+
+/*
+ Metal backend. Double buffers and throttles to vsync. "Dynamic" buffers are
+ Shared (host visible) and duplicated (due to 2 frames in flight), "static"
+ are Managed on macOS and Shared on iOS/tvOS, and still duplicated.
+ "Immutable" is like "static" but with only one native buffer underneath.
+ Textures are Private (device local) and a host visible staging buffer is
+ used to upload data to them. Does not rely on strong objects refs from
+ command buffers (hence uses commandBufferWithUnretainedReferences), but
+ does rely on automatic dependency tracking between encoders (hence no
+ MTLResourceHazardTrackingModeUntracked atm).
+*/
+
+#if __has_feature(objc_arc)
+#error ARC not supported
+#endif
+
+// Note: we expect everything here pass the Metal API validation when running
+// in Debug mode in XCode. Some of the issues that break validation are not
+// obvious and not visible when running outside XCode.
+//
+// An exception is the nextDrawable Called Early blah blah warning, which is
+// plain and simply false.
+
+/*!
+ \class QRhiMetalInitParams
+ \inmodule QtRhi
+ \brief Metal specific initialization parameters.
+
+ A Metal-based QRhi needs no special parameters for initialization.
+
+ \badcode
+ QRhiMetalInitParams params;
+ rhi = QRhi::create(QRhi::Metal, &params);
+ \endcode
+
+ \note Metal API validation cannot be enabled by the application. Instead,
+ run the debug build of the application in XCode. Generating a
+ \c{.xcodeproj} file via \c{qmake -spec macx-xcode} provides a convenient
+ way to enable this.
+
+ \note QRhiSwapChain can only target QWindow instances that have their
+ surface type set to QSurface::MetalSurface.
+
+ \section2 Working with existing Metal devices
+
+ When interoperating with another graphics engine, it may be necessary to
+ get a QRhi instance that uses the same Metal device. This can be achieved
+ by passing a pointer to a QRhiMetalNativeHandles to QRhi::create(). The
+ device must be set to a non-null value then. Optionally, a command queue
+ object can be specified as well.
+
+ The QRhi does not take ownership of any of the external objects.
+ */
+
+/*!
+ \class QRhiMetalNativeHandles
+ \inmodule QtRhi
+ \brief Holds the Metal device used by the QRhi.
+
+ \note The class uses \c{void *} as the type since including the Objective C
+ headers is not acceptable here. The actual types are \c{id<MTLDevice>} and
+ \c{id<MTLCommandQueue>}.
+ */
+
+/*!
+ \class QRhiMetalTextureNativeHandles
+ \inmodule QtRhi
+ \brief Holds the Metal texture object that is backing a QRhiTexture instance.
+
+ \note The class uses \c{void *} as the type since including the Objective C
+ headers is not acceptable here. The actual type is \c{id<MTLTexture>}.
+ */
+
+/*!
+ \class QRhiMetalCommandBufferNativeHandles
+ \inmodule QtRhi
+ \brief Holds the MTLCommandBuffer and MTLRenderCommandEncoder objects that are backing a QRhiCommandBuffer.
+
+ \note The command buffer object is only guaranteed to be valid while
+ recording a frame, that is, between a \l{QRhi::beginFrame()}{beginFrame()}
+ - \l{QRhi::endFrame()}{endFrame()} or
+ \l{QRhi::beginOffscreenFrame()}{beginOffscreenFrame()} -
+ \l{QRhi::endOffsrceenFrame()}{endOffscreenFrame()} pair.
+
+ \note The command encoder is only valid while recording a pass, that is,
+ between \l{QRhiCommandBuffer::beginPass()} -
+ \l{QRhiCommandBuffer::endPass()}.
+ */
+
+struct QRhiMetalData
+{
+ QRhiMetalData(QRhiImplementation *rhi) : ofr(rhi) { }
+
+ id<MTLDevice> dev = nil;
+ id<MTLCommandQueue> cmdQueue = nil;
+
+ MTLRenderPassDescriptor *createDefaultRenderPass(bool hasDepthStencil,
+ const QColor &colorClearValue,
+ const QRhiDepthStencilClearValue &depthStencilClearValue,
+ int colorAttCount);
+ id<MTLLibrary> createMetalLib(const QShader &shader, QShader::Variant shaderVariant,
+ QString *error, QByteArray *entryPoint);
+ id<MTLFunction> createMSLShaderFunction(id<MTLLibrary> lib, const QByteArray &entryPoint);
+
+ struct DeferredReleaseEntry {
+ enum Type {
+ Buffer,
+ RenderBuffer,
+ Texture,
+ Sampler,
+ StagingBuffer
+ };
+ Type type;
+ int lastActiveFrameSlot; // -1 if not used otherwise 0..FRAMES_IN_FLIGHT-1
+ union {
+ struct {
+ id<MTLBuffer> buffers[QMTL_FRAMES_IN_FLIGHT];
+ } buffer;
+ struct {
+ id<MTLTexture> texture;
+ } renderbuffer;
+ struct {
+ id<MTLTexture> texture;
+ id<MTLBuffer> stagingBuffers[QMTL_FRAMES_IN_FLIGHT];
+ } texture;
+ struct {
+ id<MTLSamplerState> samplerState;
+ } sampler;
+ struct {
+ id<MTLBuffer> buffer;
+ } stagingBuffer;
+ };
+ };
+ QVector<DeferredReleaseEntry> releaseQueue;
+
+ struct OffscreenFrame {
+ OffscreenFrame(QRhiImplementation *rhi) : cbWrapper(rhi) { }
+ bool active = false;
+ QMetalCommandBuffer cbWrapper;
+ } ofr;
+
+ struct ActiveReadback {
+ int activeFrameSlot = -1;
+ QRhiReadbackDescription desc;
+ QRhiReadbackResult *result;
+ id<MTLBuffer> buf;
+ quint32 bufSize;
+ QSize pixelSize;
+ QRhiTexture::Format format;
+ };
+ QVector<ActiveReadback> activeReadbacks;
+
+ API_AVAILABLE(macos(10.13), ios(11.0)) MTLCaptureManager *captureMgr;
+ API_AVAILABLE(macos(10.13), ios(11.0)) id<MTLCaptureScope> captureScope = nil;
+
+ static const int TEXBUF_ALIGN = 256; // probably not accurate
+};
+
+Q_DECLARE_TYPEINFO(QRhiMetalData::DeferredReleaseEntry, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(QRhiMetalData::ActiveReadback, Q_MOVABLE_TYPE);
+
+struct QMetalBufferData
+{
+ bool managed;
+ id<MTLBuffer> buf[QMTL_FRAMES_IN_FLIGHT];
+ QVector<QRhiResourceUpdateBatchPrivate::DynamicBufferUpdate> pendingUpdates[QMTL_FRAMES_IN_FLIGHT];
+};
+
+struct QMetalRenderBufferData
+{
+ MTLPixelFormat format;
+ id<MTLTexture> tex = nil;
+};
+
+struct QMetalTextureData
+{
+ MTLPixelFormat format;
+ id<MTLTexture> tex = nil;
+ id<MTLBuffer> stagingBuf[QMTL_FRAMES_IN_FLIGHT];
+ bool owns = true;
+};
+
+struct QMetalSamplerData
+{
+ id<MTLSamplerState> samplerState = nil;
+};
+
+struct QMetalCommandBufferData
+{
+ id<MTLCommandBuffer> cb;
+ id<MTLRenderCommandEncoder> currentPassEncoder;
+ MTLRenderPassDescriptor *currentPassRpDesc;
+ int currentFirstVertexBinding;
+ QRhiBatchedBindings<id<MTLBuffer> > currentVertexInputsBuffers;
+ QRhiBatchedBindings<NSUInteger> currentVertexInputOffsets;
+};
+
+struct QMetalRenderTargetData
+{
+ QSize pixelSize;
+ float dpr = 1;
+ int sampleCount = 1;
+ int colorAttCount = 0;
+ int dsAttCount = 0;
+
+ struct ColorAtt {
+ bool needsDrawableForTex = false;
+ id<MTLTexture> tex = nil;
+ int layer = 0;
+ int level = 0;
+ bool needsDrawableForResolveTex = false;
+ id<MTLTexture> resolveTex = nil;
+ int resolveLayer = 0;
+ int resolveLevel = 0;
+ };
+
+ struct {
+ ColorAtt colorAtt[QMetalRenderPassDescriptor::MAX_COLOR_ATTACHMENTS];
+ id<MTLTexture> dsTex = nil;
+ bool hasStencil = false;
+ bool depthNeedsStore = false;
+ } fb;
+};
+
+struct QMetalGraphicsPipelineData
+{
+ id<MTLRenderPipelineState> ps = nil;
+ id<MTLDepthStencilState> ds = nil;
+ MTLPrimitiveType primitiveType;
+ MTLWinding winding;
+ MTLCullMode cullMode;
+ id<MTLLibrary> vsLib = nil;
+ id<MTLFunction> vsFunc = nil;
+ id<MTLLibrary> fsLib = nil;
+ id<MTLFunction> fsFunc = nil;
+};
+
+struct QMetalSwapChainData
+{
+ CAMetalLayer *layer = nullptr;
+ id<CAMetalDrawable> curDrawable;
+ dispatch_semaphore_t sem[QMTL_FRAMES_IN_FLIGHT];
+ MTLRenderPassDescriptor *rp = nullptr;
+ id<MTLTexture> msaaTex[QMTL_FRAMES_IN_FLIGHT];
+ QRhiTexture::Format rhiColorFormat;
+ MTLPixelFormat colorFormat;
+};
+
+QRhiMetal::QRhiMetal(QRhiMetalInitParams *params, QRhiMetalNativeHandles *importDevice)
+{
+ Q_UNUSED(params);
+
+ d = new QRhiMetalData(this);
+
+ importedDevice = importDevice != nullptr;
+ if (importedDevice) {
+ if (d->dev) {
+ d->dev = (id<MTLDevice>) importDevice->dev;
+ importedCmdQueue = importDevice->cmdQueue != nullptr;
+ if (importedCmdQueue)
+ d->cmdQueue = (id<MTLCommandQueue>) importDevice->cmdQueue;
+ } else {
+ qWarning("No MTLDevice given, cannot import");
+ importedDevice = false;
+ }
+ }
+}
+
+QRhiMetal::~QRhiMetal()
+{
+ delete d;
+}
+
+static inline uint aligned(uint v, uint byteAlign)
+{
+ return (v + byteAlign - 1) & ~(byteAlign - 1);
+}
+
+bool QRhiMetal::create(QRhi::Flags flags)
+{
+ Q_UNUSED(flags);
+
+ if (importedDevice)
+ [d->dev retain];
+ else
+ d->dev = MTLCreateSystemDefaultDevice();
+
+ qDebug("Metal device: %s", qPrintable(QString::fromNSString([d->dev name])));
+
+ if (importedCmdQueue)
+ [d->cmdQueue retain];
+ else
+ d->cmdQueue = [d->dev newCommandQueue];
+
+ if (@available(macOS 10.13, iOS 11.0, *)) {
+ d->captureMgr = [MTLCaptureManager sharedCaptureManager];
+ // Have a custom capture scope as well which then shows up in XCode as
+ // an option when capturing, and becomes especially useful when having
+ // multiple windows with multiple QRhis.
+ d->captureScope = [d->captureMgr newCaptureScopeWithCommandQueue: d->cmdQueue];
+ const QString label = QString::asprintf("Qt capture scope for QRhi %p", this);
+ d->captureScope.label = label.toNSString();
+ }
+
+#if defined(Q_OS_MACOS)
+ caps.maxTextureSize = 16384;
+#elif defined(Q_OS_TVOS)
+ if ([d->dev supportsFeatureSet: MTLFeatureSet(30003)]) // MTLFeatureSet_tvOS_GPUFamily2_v1
+ caps.maxTextureSize = 16384;
+ else
+ caps.maxTextureSize = 8192;
+#elif defined(Q_OS_IOS)
+ // welcome to feature set hell
+ if ([d->dev supportsFeatureSet: MTLFeatureSet(16)] // MTLFeatureSet_iOS_GPUFamily5_v1
+ || [d->dev supportsFeatureSet: MTLFeatureSet(11)] // MTLFeatureSet_iOS_GPUFamily4_v1
+ || [d->dev supportsFeatureSet: MTLFeatureSet(4)]) // MTLFeatureSet_iOS_GPUFamily3_v1
+ {
+ caps.maxTextureSize = 16384;
+ } else if ([d->dev supportsFeatureSet: MTLFeatureSet(3)] // MTLFeatureSet_iOS_GPUFamily2_v2
+ || [d->dev supportsFeatureSet: MTLFeatureSet(2)]) // MTLFeatureSet_iOS_GPUFamily1_v2
+ {
+ caps.maxTextureSize = 8192;
+ } else {
+ caps.maxTextureSize = 4096;
+ }
+#endif
+
+ nativeHandlesStruct.dev = d->dev;
+ nativeHandlesStruct.cmdQueue = d->cmdQueue;
+
+ return true;
+}
+
+void QRhiMetal::destroy()
+{
+ executeDeferredReleases(true);
+ finishActiveReadbacks(true);
+
+ if (@available(macOS 10.13, iOS 11.0, *)) {
+ [d->captureScope release];
+ d->captureScope = nil;
+ }
+
+ [d->cmdQueue release];
+ if (!importedCmdQueue)
+ d->cmdQueue = nil;
+
+ [d->dev release];
+ if (!importedDevice)
+ d->dev = nil;
+}
+
+QVector<int> QRhiMetal::supportedSampleCounts() const
+{
+ return { 1, 2, 4, 8 };
+}
+
+int QRhiMetal::effectiveSampleCount(int sampleCount) const
+{
+ // Stay compatible with QSurfaceFormat and friends where samples == 0 means the same as 1.
+ const int s = qBound(1, sampleCount, 64);
+ if (!supportedSampleCounts().contains(s)) {
+ qWarning("Attempted to set unsupported sample count %d", sampleCount);
+ return 1;
+ }
+ return s;
+}
+
+QRhiSwapChain *QRhiMetal::createSwapChain()
+{
+ return new QMetalSwapChain(this);
+}
+
+QRhiBuffer *QRhiMetal::createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, int size)
+{
+ return new QMetalBuffer(this, type, usage, size);
+}
+
+int QRhiMetal::ubufAlignment() const
+{
+ return 256;
+}
+
+bool QRhiMetal::isYUpInFramebuffer() const
+{
+ return false;
+}
+
+bool QRhiMetal::isYUpInNDC() const
+{
+ return true;
+}
+
+bool QRhiMetal::isClipDepthZeroToOne() const
+{
+ return true;
+}
+
+QMatrix4x4 QRhiMetal::clipSpaceCorrMatrix() const
+{
+ // depth range 0..1
+ static QMatrix4x4 m;
+ if (m.isIdentity()) {
+ // NB the ctor takes row-major
+ m = QMatrix4x4(1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 0.5f, 0.5f,
+ 0.0f, 0.0f, 0.0f, 1.0f);
+ }
+ return m;
+}
+
+bool QRhiMetal::isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const
+{
+ Q_UNUSED(flags);
+
+#ifdef Q_OS_MACOS
+ if (format >= QRhiTexture::ETC2_RGB8 && format <= QRhiTexture::ETC2_RGBA8)
+ return false;
+ if (format >= QRhiTexture::ASTC_4x4 && format <= QRhiTexture::ASTC_12x12)
+ return false;
+#else
+ if (format >= QRhiTexture::BC1 && format <= QRhiTexture::BC7)
+ return false;
+#endif
+
+ return true;
+}
+
+bool QRhiMetal::isFeatureSupported(QRhi::Feature feature) const
+{
+ switch (feature) {
+ case QRhi::MultisampleTexture:
+ return true;
+ case QRhi::MultisampleRenderBuffer:
+ return true;
+ case QRhi::DebugMarkers:
+ return true;
+ case QRhi::Timestamps:
+ return false;
+ case QRhi::Instancing:
+ return true;
+ case QRhi::CustomInstanceStepRate:
+ return true;
+ case QRhi::PrimitiveRestart:
+ return true;
+ case QRhi::NonDynamicUniformBuffers:
+ return true;
+ case QRhi::NonFourAlignedEffectiveIndexBufferOffset:
+ return false;
+ case QRhi::NPOTTextureRepeat:
+ return true;
+ case QRhi::RedOrAlpha8IsRed:
+ return true;
+ case QRhi::ElementIndexUint:
+ return true;
+ default:
+ Q_UNREACHABLE();
+ return false;
+ }
+}
+
+int QRhiMetal::resourceLimit(QRhi::ResourceLimit limit) const
+{
+ switch (limit) {
+ case QRhi::TextureSizeMin:
+ return 1;
+ case QRhi::TextureSizeMax:
+ return caps.maxTextureSize;
+ case QRhi::MaxColorAttachments:
+ return 8;
+ case QRhi::FramesInFlight:
+ return QMTL_FRAMES_IN_FLIGHT;
+ default:
+ Q_UNREACHABLE();
+ return 0;
+ }
+}
+
+const QRhiNativeHandles *QRhiMetal::nativeHandles()
+{
+ return &nativeHandlesStruct;
+}
+
+void QRhiMetal::sendVMemStatsToProfiler()
+{
+ // nothing to do here
+}
+
+QRhiRenderBuffer *QRhiMetal::createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize,
+ int sampleCount, QRhiRenderBuffer::Flags flags)
+{
+ return new QMetalRenderBuffer(this, type, pixelSize, sampleCount, flags);
+}
+
+QRhiTexture *QRhiMetal::createTexture(QRhiTexture::Format format, const QSize &pixelSize,
+ int sampleCount, QRhiTexture::Flags flags)
+{
+ return new QMetalTexture(this, format, pixelSize, sampleCount, flags);
+}
+
+QRhiSampler *QRhiMetal::createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
+ QRhiSampler::Filter mipmapMode,
+ QRhiSampler::AddressMode u, QRhiSampler::AddressMode v)
+{
+ return new QMetalSampler(this, magFilter, minFilter, mipmapMode, u, v);
+}
+
+QRhiTextureRenderTarget *QRhiMetal::createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
+ QRhiTextureRenderTarget::Flags flags)
+{
+ return new QMetalTextureRenderTarget(this, desc, flags);
+}
+
+QRhiGraphicsPipeline *QRhiMetal::createGraphicsPipeline()
+{
+ return new QMetalGraphicsPipeline(this);
+}
+
+QRhiShaderResourceBindings *QRhiMetal::createShaderResourceBindings()
+{
+ return new QMetalShaderResourceBindings(this);
+}
+
+void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD, QMetalCommandBuffer *cbD,
+ int dynamicOffsetCount,
+ const QRhiCommandBuffer::DynamicOffset *dynamicOffsets,
+ bool offsetOnlyChange)
+{
+ static const int KNOWN_STAGES = 2;
+ struct {
+ QRhiBatchedBindings<id<MTLBuffer> > buffers;
+ QRhiBatchedBindings<NSUInteger> bufferOffsets;
+ QRhiBatchedBindings<id<MTLTexture> > textures;
+ QRhiBatchedBindings<id<MTLSamplerState> > samplers;
+ } res[KNOWN_STAGES];
+
+ for (const QRhiShaderResourceBinding &binding : qAsConst(srbD->sortedBindings)) {
+ const QRhiShaderResourceBindingPrivate *b = QRhiShaderResourceBindingPrivate::get(&binding);
+ switch (b->type) {
+ case QRhiShaderResourceBinding::UniformBuffer:
+ {
+ QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, b->u.ubuf.buf);
+ id<MTLBuffer> mtlbuf = bufD->d->buf[bufD->m_type == QRhiBuffer::Immutable ? 0 : currentFrameSlot];
+ uint offset = b->u.ubuf.offset;
+ for (int i = 0; i < dynamicOffsetCount; ++i) {
+ const QRhiCommandBuffer::DynamicOffset &dynOfs(dynamicOffsets[i]);
+ if (dynOfs.first == b->binding) {
+ offset = dynOfs.second;
+ break;
+ }
+ }
+ if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) {
+ res[0].buffers.feed(b->binding, mtlbuf);
+ res[0].bufferOffsets.feed(b->binding, offset);
+ }
+ if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
+ res[1].buffers.feed(b->binding, mtlbuf);
+ res[1].bufferOffsets.feed(b->binding, offset);
+ }
+ }
+ break;
+ case QRhiShaderResourceBinding::SampledTexture:
+ {
+ QMetalTexture *texD = QRHI_RES(QMetalTexture, b->u.stex.tex);
+ QMetalSampler *samplerD = QRHI_RES(QMetalSampler, b->u.stex.sampler);
+ if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) {
+ res[0].textures.feed(b->binding, texD->d->tex);
+ res[0].samplers.feed(b->binding, samplerD->d->samplerState);
+ }
+ if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
+ res[1].textures.feed(b->binding, texD->d->tex);
+ res[1].samplers.feed(b->binding, samplerD->d->samplerState);
+ }
+ }
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ }
+
+ for (int idx = 0; idx < KNOWN_STAGES; ++idx) {
+ res[idx].buffers.finish();
+ res[idx].bufferOffsets.finish();
+
+ for (int i = 0, ie = res[idx].buffers.batches.count(); i != ie; ++i) {
+ const auto &bufferBatch(res[idx].buffers.batches[i]);
+ const auto &offsetBatch(res[idx].bufferOffsets.batches[i]);
+ switch (idx) {
+ case 0:
+ [cbD->d->currentPassEncoder setVertexBuffers: bufferBatch.resources.constData()
+ offsets: offsetBatch.resources.constData()
+ withRange: NSMakeRange(bufferBatch.startBinding, bufferBatch.resources.count())];
+ break;
+ case 1:
+ [cbD->d->currentPassEncoder setFragmentBuffers: bufferBatch.resources.constData()
+ offsets: offsetBatch.resources.constData()
+ withRange: NSMakeRange(bufferBatch.startBinding, bufferBatch.resources.count())];
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ }
+
+ if (offsetOnlyChange)
+ continue;
+
+ res[idx].textures.finish();
+ res[idx].samplers.finish();
+
+ for (int i = 0, ie = res[idx].textures.batches.count(); i != ie; ++i) {
+ const auto &batch(res[idx].textures.batches[i]);
+ switch (idx) {
+ case 0:
+ [cbD->d->currentPassEncoder setVertexTextures: batch.resources.constData()
+ withRange: NSMakeRange(batch.startBinding, batch.resources.count())];
+ break;
+ case 1:
+ [cbD->d->currentPassEncoder setFragmentTextures: batch.resources.constData()
+ withRange: NSMakeRange(batch.startBinding, batch.resources.count())];
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ }
+ for (int i = 0, ie = res[idx].samplers.batches.count(); i != ie; ++i) {
+ const auto &batch(res[idx].samplers.batches[i]);
+ switch (idx) {
+ case 0:
+ [cbD->d->currentPassEncoder setVertexSamplerStates: batch.resources.constData()
+ withRange: NSMakeRange(batch.startBinding, batch.resources.count())];
+ break;
+ case 1:
+ [cbD->d->currentPassEncoder setFragmentSamplerStates: batch.resources.constData()
+ withRange: NSMakeRange(batch.startBinding, batch.resources.count())];
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ }
+ }
+}
+
+void QRhiMetal::setGraphicsPipeline(QRhiCommandBuffer *cb, QRhiGraphicsPipeline *ps)
+{
+ Q_ASSERT(inPass);
+
+ QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb);
+ QMetalGraphicsPipeline *psD = QRHI_RES(QMetalGraphicsPipeline, ps);
+
+ if (cbD->currentPipeline != ps || cbD->currentPipelineGeneration != psD->generation) {
+ cbD->currentPipeline = ps;
+ cbD->currentPipelineGeneration = psD->generation;
+
+ [cbD->d->currentPassEncoder setRenderPipelineState: psD->d->ps];
+ [cbD->d->currentPassEncoder setDepthStencilState: psD->d->ds];
+ [cbD->d->currentPassEncoder setCullMode: psD->d->cullMode];
+ [cbD->d->currentPassEncoder setFrontFacingWinding: psD->d->winding];
+ }
+
+ psD->lastActiveFrameSlot = currentFrameSlot;
+}
+
+void QRhiMetal::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBindings *srb,
+ int dynamicOffsetCount,
+ const QRhiCommandBuffer::DynamicOffset *dynamicOffsets)
+{
+ Q_ASSERT(inPass);
+
+ QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb);
+ Q_ASSERT(cbD->currentPipeline);
+ if (!srb)
+ srb = QRHI_RES(QMetalGraphicsPipeline, cbD->currentPipeline)->m_shaderResourceBindings;
+
+ QMetalShaderResourceBindings *srbD = QRHI_RES(QMetalShaderResourceBindings, srb);
+ bool hasSlottedResourceInSrb = false;
+ bool hasDynamicOffsetInSrb = false;
+ bool resNeedsRebind = false;
+
+ // do buffer writes, figure out if we need to rebind, and mark as in-use
+ for (int i = 0, ie = srbD->sortedBindings.count(); i != ie; ++i) {
+ const QRhiShaderResourceBindingPrivate *b = QRhiShaderResourceBindingPrivate::get(&srbD->sortedBindings[i]);
+ QMetalShaderResourceBindings::BoundResourceData &bd(srbD->boundResourceData[i]);
+ switch (b->type) {
+ case QRhiShaderResourceBinding::UniformBuffer:
+ {
+ QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, b->u.ubuf.buf);
+ Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer));
+ executeBufferHostWritesForCurrentFrame(bufD);
+ if (bufD->m_type != QRhiBuffer::Immutable)
+ hasSlottedResourceInSrb = true;
+ if (b->u.ubuf.hasDynamicOffset)
+ hasDynamicOffsetInSrb = true;
+ if (bufD->generation != bd.ubuf.generation || bufD->m_id != bd.ubuf.id) {
+ resNeedsRebind = true;
+ bd.ubuf.id = bufD->m_id;
+ bd.ubuf.generation = bufD->generation;
+ }
+ bufD->lastActiveFrameSlot = currentFrameSlot;
+ }
+ break;
+ case QRhiShaderResourceBinding::SampledTexture:
+ {
+ QMetalTexture *texD = QRHI_RES(QMetalTexture, b->u.stex.tex);
+ QMetalSampler *samplerD = QRHI_RES(QMetalSampler, b->u.stex.sampler);
+ if (texD->generation != bd.stex.texGeneration
+ || texD->m_id != bd.stex.texId
+ || samplerD->generation != bd.stex.samplerGeneration
+ || samplerD->m_id != bd.stex.samplerId)
+ {
+ resNeedsRebind = true;
+ bd.stex.texId = texD->m_id;
+ bd.stex.texGeneration = texD->generation;
+ bd.stex.samplerId = samplerD->m_id;
+ bd.stex.samplerGeneration = samplerD->generation;
+ }
+ texD->lastActiveFrameSlot = currentFrameSlot;
+ samplerD->lastActiveFrameSlot = currentFrameSlot;
+ }
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ }
+
+ // make sure the resources for the correct slot get bound
+ const int resSlot = hasSlottedResourceInSrb ? currentFrameSlot : 0;
+ if (hasSlottedResourceInSrb && cbD->currentResSlot != resSlot)
+ resNeedsRebind = true;
+
+ const bool srbChange = cbD->currentSrb != srb || cbD->currentSrbGeneration != srbD->generation;
+
+ // dynamic uniform buffer offsets always trigger a rebind
+ if (hasDynamicOffsetInSrb || resNeedsRebind || srbChange) {
+ cbD->currentSrb = srb;
+ cbD->currentSrbGeneration = srbD->generation;
+ cbD->currentResSlot = resSlot;
+
+ const bool offsetOnlyChange = hasDynamicOffsetInSrb && !resNeedsRebind && !srbChange;
+ enqueueShaderResourceBindings(srbD, cbD, dynamicOffsetCount, dynamicOffsets, offsetOnlyChange);
+ }
+}
+
+void QRhiMetal::setVertexInput(QRhiCommandBuffer *cb,
+ int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings,
+ QRhiBuffer *indexBuf, quint32 indexOffset, QRhiCommandBuffer::IndexFormat indexFormat)
+{
+ Q_ASSERT(inPass);
+ QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb);
+ Q_ASSERT(cbD->currentPipeline);
+
+ QRhiBatchedBindings<id<MTLBuffer> > buffers;
+ QRhiBatchedBindings<NSUInteger> offsets;
+ for (int i = 0; i < bindingCount; ++i) {
+ QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, bindings[i].first);
+ executeBufferHostWritesForCurrentFrame(bufD);
+ bufD->lastActiveFrameSlot = currentFrameSlot;
+ id<MTLBuffer> mtlbuf = bufD->d->buf[bufD->m_type == QRhiBuffer::Immutable ? 0 : currentFrameSlot];
+ buffers.feed(startBinding + i, mtlbuf);
+ offsets.feed(startBinding + i, bindings[i].second);
+ }
+ buffers.finish();
+ offsets.finish();
+
+ // same binding space for vertex and constant buffers - work it around
+ QRhiShaderResourceBindings *srb = cbD->currentSrb;
+ // There's nothing guaranteeing setShaderResources() was called before
+ // setVertexInput()... but whatever srb will get bound will have to be
+ // layout-compatible anyways so maxBinding is the same.
+ if (!srb)
+ srb = cbD->currentPipeline->shaderResourceBindings();
+ const int firstVertexBinding = QRHI_RES(QMetalShaderResourceBindings, srb)->maxBinding + 1;
+
+ if (firstVertexBinding != cbD->d->currentFirstVertexBinding
+ || buffers != cbD->d->currentVertexInputsBuffers
+ || offsets != cbD->d->currentVertexInputOffsets)
+ {
+ cbD->d->currentFirstVertexBinding = firstVertexBinding;
+ cbD->d->currentVertexInputsBuffers = buffers;
+ cbD->d->currentVertexInputOffsets = offsets;
+
+ for (int i = 0, ie = buffers.batches.count(); i != ie; ++i) {
+ const auto &bufferBatch(buffers.batches[i]);
+ const auto &offsetBatch(offsets.batches[i]);
+ [cbD->d->currentPassEncoder setVertexBuffers:
+ bufferBatch.resources.constData()
+ offsets: offsetBatch.resources.constData()
+ withRange: NSMakeRange(firstVertexBinding + bufferBatch.startBinding, bufferBatch.resources.count())];
+ }
+ }
+
+ if (indexBuf) {
+ QMetalBuffer *ibufD = QRHI_RES(QMetalBuffer, indexBuf);
+ executeBufferHostWritesForCurrentFrame(ibufD);
+ ibufD->lastActiveFrameSlot = currentFrameSlot;
+ cbD->currentIndexBuffer = indexBuf;
+ cbD->currentIndexOffset = indexOffset;
+ cbD->currentIndexFormat = indexFormat;
+ } else {
+ cbD->currentIndexBuffer = nullptr;
+ }
+}
+
+void QRhiMetal::setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport)
+{
+ Q_ASSERT(inPass);
+ QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb);
+ Q_ASSERT(cbD->currentPipeline && cbD->currentTarget);
+ const QSize outputSize = cbD->currentTarget->pixelSize();
+
+ // x,y is top-left in MTLViewportRect but bottom-left in QRhiViewport
+ float x, y, w, h;
+ if (!qrhi_toTopLeftRenderTargetRect(outputSize, viewport.viewport(), &x, &y, &w, &h))
+ return;
+
+ MTLViewport vp;
+ vp.originX = x;
+ vp.originY = y;
+ vp.width = w;
+ vp.height = h;
+ vp.znear = viewport.minDepth();
+ vp.zfar = viewport.maxDepth();
+
+ [cbD->d->currentPassEncoder setViewport: vp];
+
+ if (!QRHI_RES(QMetalGraphicsPipeline, cbD->currentPipeline)->m_flags.testFlag(QRhiGraphicsPipeline::UsesScissor)) {
+ MTLScissorRect s;
+ s.x = x;
+ s.y = y;
+ s.width = w;
+ s.height = h;
+ [cbD->d->currentPassEncoder setScissorRect: s];
+ }
+}
+
+void QRhiMetal::setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor)
+{
+ Q_ASSERT(inPass);
+ QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb);
+ Q_ASSERT(cbD->currentPipeline && cbD->currentTarget);
+ Q_ASSERT(QRHI_RES(QMetalGraphicsPipeline, cbD->currentPipeline)->m_flags.testFlag(QRhiGraphicsPipeline::UsesScissor));
+ const QSize outputSize = cbD->currentTarget->pixelSize();
+
+ // x,y is top-left in MTLScissorRect but bottom-left in QRhiScissor
+ int x, y, w, h;
+ if (!qrhi_toTopLeftRenderTargetRect(outputSize, scissor.scissor(), &x, &y, &w, &h))
+ return;
+
+ MTLScissorRect s;
+ s.x = x;
+ s.y = y;
+ s.width = w;
+ s.height = h;
+
+ [cbD->d->currentPassEncoder setScissorRect: s];
+}
+
+void QRhiMetal::setBlendConstants(QRhiCommandBuffer *cb, const QColor &c)
+{
+ Q_ASSERT(inPass);
+ QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb);
+ [cbD->d->currentPassEncoder setBlendColorRed: c.redF() green: c.greenF() blue: c.blueF() alpha: c.alphaF()];
+}
+
+void QRhiMetal::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue)
+{
+ Q_ASSERT(inPass);
+ QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb);
+ [cbD->d->currentPassEncoder setStencilReferenceValue: refValue];
+}
+
+void QRhiMetal::draw(QRhiCommandBuffer *cb, quint32 vertexCount,
+ quint32 instanceCount, quint32 firstVertex, quint32 firstInstance)
+{
+ Q_ASSERT(inPass);
+ QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb);
+ [cbD->d->currentPassEncoder drawPrimitives:
+ QRHI_RES(QMetalGraphicsPipeline, cbD->currentPipeline)->d->primitiveType
+ vertexStart: firstVertex vertexCount: vertexCount instanceCount: instanceCount baseInstance: firstInstance];
+}
+
+void QRhiMetal::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
+ quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance)
+{
+ Q_ASSERT(inPass);
+ QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb);
+ if (!cbD->currentIndexBuffer)
+ return;
+
+ const quint32 indexOffset = cbD->currentIndexOffset + firstIndex * (cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? 2 : 4);
+ Q_ASSERT(indexOffset == aligned(indexOffset, 4));
+
+ QMetalBuffer *ibufD = QRHI_RES(QMetalBuffer, cbD->currentIndexBuffer);
+ id<MTLBuffer> mtlbuf = ibufD->d->buf[ibufD->m_type == QRhiBuffer::Immutable ? 0 : currentFrameSlot];
+
+ [cbD->d->currentPassEncoder drawIndexedPrimitives: QRHI_RES(QMetalGraphicsPipeline, cbD->currentPipeline)->d->primitiveType
+ indexCount: indexCount
+ indexType: cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32
+ indexBuffer: mtlbuf
+ indexBufferOffset: indexOffset
+ instanceCount: instanceCount
+ baseVertex: vertexOffset
+ baseInstance: firstInstance];
+}
+
+void QRhiMetal::debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name)
+{
+ if (!debugMarkers)
+ return;
+
+ NSString *str = [NSString stringWithUTF8String: name.constData()];
+ QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb);
+ if (inPass) {
+ [cbD->d->currentPassEncoder pushDebugGroup: str];
+ } else {
+ if (@available(macOS 10.13, iOS 11.0, *))
+ [cbD->d->cb pushDebugGroup: str];
+ }
+}
+
+void QRhiMetal::debugMarkEnd(QRhiCommandBuffer *cb)
+{
+ if (!debugMarkers)
+ return;
+
+ QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb);
+ if (inPass) {
+ [cbD->d->currentPassEncoder popDebugGroup];
+ } else {
+ if (@available(macOS 10.13, iOS 11.0, *))
+ [cbD->d->cb popDebugGroup];
+ }
+}
+
+void QRhiMetal::debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg)
+{
+ if (!debugMarkers)
+ return;
+
+ if (inPass) {
+ QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb);
+ [cbD->d->currentPassEncoder insertDebugSignpost: [NSString stringWithUTF8String: msg.constData()]];
+ }
+}
+
+const QRhiNativeHandles *QRhiMetal::nativeHandles(QRhiCommandBuffer *cb)
+{
+ return QRHI_RES(QMetalCommandBuffer, cb)->nativeHandles();
+}
+
+void QRhiMetal::beginExternal(QRhiCommandBuffer *cb)
+{
+ Q_UNUSED(cb);
+}
+
+void QRhiMetal::endExternal(QRhiCommandBuffer *cb)
+{
+ QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb);
+ cbD->resetPerPassCachedState();
+}
+
+QRhi::FrameOpResult QRhiMetal::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags)
+{
+ Q_UNUSED(flags);
+ Q_ASSERT(!inFrame);
+ inFrame = true;
+
+ QMetalSwapChain *swapChainD = QRHI_RES(QMetalSwapChain, swapChain);
+
+ // This is a bit messed up since for this swapchain we want to wait for the
+ // commands+present to complete, while for others just for the commands
+ // (for this same frame slot) but not sure how to do that in a sane way so
+ // wait for full cb completion for now.
+ for (QMetalSwapChain *sc : qAsConst(swapchains)) {
+ dispatch_semaphore_t sem = sc->d->sem[swapChainD->currentFrameSlot];
+ dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
+ if (sc != swapChainD)
+ dispatch_semaphore_signal(sem);
+ }
+
+ currentSwapChain = swapChainD;
+ currentFrameSlot = swapChainD->currentFrameSlot;
+ if (swapChainD->ds)
+ swapChainD->ds->lastActiveFrameSlot = currentFrameSlot;
+
+ if (@available(macOS 10.13, iOS 11.0, *))
+ [d->captureScope beginScope];
+
+ // Do not let the command buffer mess with the refcount of objects. We do
+ // have a proper render loop and will manage lifetimes similarly to other
+ // backends (Vulkan).
+ swapChainD->cbWrapper.d->cb = [d->cmdQueue commandBufferWithUnretainedReferences];
+
+ QMetalRenderTargetData::ColorAtt colorAtt;
+ if (swapChainD->samples > 1) {
+ colorAtt.tex = swapChainD->d->msaaTex[currentFrameSlot];
+ colorAtt.needsDrawableForResolveTex = true;
+ } else {
+ colorAtt.needsDrawableForTex = true;
+ }
+
+ swapChainD->rtWrapper.d->fb.colorAtt[0] = colorAtt;
+ swapChainD->rtWrapper.d->fb.dsTex = swapChainD->ds ? swapChainD->ds->d->tex : nil;
+ swapChainD->rtWrapper.d->fb.hasStencil = swapChainD->ds ? true : false;
+ swapChainD->rtWrapper.d->fb.depthNeedsStore = false;
+
+ QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
+ QRHI_PROF_F(beginSwapChainFrame(swapChain));
+
+ executeDeferredReleases();
+ swapChainD->cbWrapper.resetState();
+ finishActiveReadbacks();
+
+ return QRhi::FrameOpSuccess;
+}
+
+QRhi::FrameOpResult QRhiMetal::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags)
+{
+ Q_ASSERT(inFrame);
+ inFrame = false;
+
+ QMetalSwapChain *swapChainD = QRHI_RES(QMetalSwapChain, swapChain);
+ Q_ASSERT(currentSwapChain == swapChainD);
+
+ const bool needsPresent = !flags.testFlag(QRhi::SkipPresent);
+ if (needsPresent)
+ [swapChainD->cbWrapper.d->cb presentDrawable: swapChainD->d->curDrawable];
+
+ __block int thisFrameSlot = currentFrameSlot;
+ [swapChainD->cbWrapper.d->cb addCompletedHandler: ^(id<MTLCommandBuffer>) {
+ dispatch_semaphore_signal(swapChainD->d->sem[thisFrameSlot]);
+ }];
+
+ [swapChainD->cbWrapper.d->cb commit];
+
+ QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
+ QRHI_PROF_F(endSwapChainFrame(swapChain, swapChainD->frameCount + 1));
+
+ if (@available(macOS 10.13, iOS 11.0, *))
+ [d->captureScope endScope];
+
+ if (needsPresent)
+ swapChainD->currentFrameSlot = (swapChainD->currentFrameSlot + 1) % QMTL_FRAMES_IN_FLIGHT;
+
+ swapChainD->frameCount += 1;
+ currentSwapChain = nullptr;
+ return QRhi::FrameOpSuccess;
+}
+
+QRhi::FrameOpResult QRhiMetal::beginOffscreenFrame(QRhiCommandBuffer **cb)
+{
+ Q_ASSERT(!inFrame);
+ inFrame = true;
+
+ currentFrameSlot = (currentFrameSlot + 1) % QMTL_FRAMES_IN_FLIGHT;
+ if (swapchains.count() > 1) {
+ for (QMetalSwapChain *sc : qAsConst(swapchains)) {
+ // wait+signal is the general pattern to ensure the commands for a
+ // given frame slot have completed (if sem is 1, we go 0 then 1; if
+ // sem is 0 we go -1, block, completion increments to 0, then us to 1)
+ dispatch_semaphore_t sem = sc->d->sem[currentFrameSlot];
+ dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
+ dispatch_semaphore_signal(sem);
+ }
+ }
+
+ d->ofr.active = true;
+ *cb = &d->ofr.cbWrapper;
+ d->ofr.cbWrapper.d->cb = [d->cmdQueue commandBufferWithUnretainedReferences];
+
+ executeDeferredReleases();
+ d->ofr.cbWrapper.resetState();
+ finishActiveReadbacks();
+
+ return QRhi::FrameOpSuccess;
+}
+
+QRhi::FrameOpResult QRhiMetal::endOffscreenFrame()
+{
+ Q_ASSERT(d->ofr.active);
+ d->ofr.active = false;
+ Q_ASSERT(inFrame);
+ inFrame = false;
+
+ [d->ofr.cbWrapper.d->cb commit];
+
+ // offscreen frames wait for completion, unlike swapchain ones
+ [d->ofr.cbWrapper.d->cb waitUntilCompleted];
+
+ finishActiveReadbacks(true);
+
+ return QRhi::FrameOpSuccess;
+}
+
+QRhi::FrameOpResult QRhiMetal::finish()
+{
+ Q_ASSERT(!inPass);
+
+ id<MTLCommandBuffer> cb = nil;
+ QMetalSwapChain *swapChainD = nullptr;
+ if (inFrame) {
+ if (d->ofr.active) {
+ Q_ASSERT(!currentSwapChain);
+ cb = d->ofr.cbWrapper.d->cb;
+ } else {
+ Q_ASSERT(currentSwapChain);
+ swapChainD = currentSwapChain;
+ cb = swapChainD->cbWrapper.d->cb;
+ }
+ }
+
+ for (QMetalSwapChain *sc : qAsConst(swapchains)) {
+ for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
+ if (currentSwapChain && sc == currentSwapChain && i == currentFrameSlot) {
+ // no wait as this is the thing we're going to be commit below and
+ // beginFrame decremented sem already and going to be signaled by endFrame
+ continue;
+ }
+ dispatch_semaphore_t sem = sc->d->sem[i];
+ dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
+ dispatch_semaphore_signal(sem);
+ }
+ }
+
+ if (cb) {
+ [cb commit];
+ [cb waitUntilCompleted];
+ }
+
+ if (inFrame) {
+ if (d->ofr.active)
+ d->ofr.cbWrapper.d->cb = [d->cmdQueue commandBufferWithUnretainedReferences];
+ else
+ swapChainD->cbWrapper.d->cb = [d->cmdQueue commandBufferWithUnretainedReferences];
+ }
+
+ executeDeferredReleases(true);
+
+ finishActiveReadbacks(true);
+
+ return QRhi::FrameOpSuccess;
+}
+
+MTLRenderPassDescriptor *QRhiMetalData::createDefaultRenderPass(bool hasDepthStencil,
+ const QColor &colorClearValue,
+ const QRhiDepthStencilClearValue &depthStencilClearValue,
+ int colorAttCount)
+{
+ MTLRenderPassDescriptor *rp = [MTLRenderPassDescriptor renderPassDescriptor];
+ MTLClearColor c = MTLClearColorMake(colorClearValue.redF(), colorClearValue.greenF(), colorClearValue.blueF(),
+ colorClearValue.alphaF());
+
+ for (int i = 0; i < colorAttCount; ++i) {
+ rp.colorAttachments[i].loadAction = MTLLoadActionClear;
+ rp.colorAttachments[i].storeAction = MTLStoreActionStore;
+ rp.colorAttachments[i].clearColor = c;
+ }
+
+ if (hasDepthStencil) {
+ rp.depthAttachment.loadAction = MTLLoadActionClear;
+ rp.depthAttachment.storeAction = MTLStoreActionDontCare;
+ rp.stencilAttachment.loadAction = MTLLoadActionClear;
+ rp.stencilAttachment.storeAction = MTLStoreActionDontCare;
+ rp.depthAttachment.clearDepth = depthStencilClearValue.depthClearValue();
+ rp.stencilAttachment.clearStencil = depthStencilClearValue.stencilClearValue();
+ }
+
+ return rp;
+}
+
+qsizetype QRhiMetal::subresUploadByteSize(const QRhiTextureSubresourceUploadDescription &subresDesc) const
+{
+ qsizetype size = 0;
+ const qsizetype imageSizeBytes = subresDesc.image().isNull() ?
+ subresDesc.data().size() : subresDesc.image().sizeInBytes();
+ if (imageSizeBytes > 0)
+ size += aligned(imageSizeBytes, QRhiMetalData::TEXBUF_ALIGN);
+ return size;
+}
+
+void QRhiMetal::enqueueSubresUpload(QMetalTexture *texD, void *mp, void *blitEncPtr,
+ int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc,
+ qsizetype *curOfs)
+{
+ const QPoint dp = subresDesc.destinationTopLeft();
+ const QByteArray rawData = subresDesc.data();
+ QImage img = subresDesc.image();
+ id<MTLBlitCommandEncoder> blitEnc = (id<MTLBlitCommandEncoder>) blitEncPtr;
+
+ if (!img.isNull()) {
+ const qsizetype fullImageSizeBytes = img.sizeInBytes();
+ int w = img.width();
+ int h = img.height();
+ int bpl = img.bytesPerLine();
+ int srcOffset = 0;
+
+ if (!subresDesc.sourceSize().isEmpty() || !subresDesc.sourceTopLeft().isNull()) {
+ const int sx = subresDesc.sourceTopLeft().x();
+ const int sy = subresDesc.sourceTopLeft().y();
+ if (!subresDesc.sourceSize().isEmpty()) {
+ w = subresDesc.sourceSize().width();
+ h = subresDesc.sourceSize().height();
+ }
+ if (img.depth() == 32) {
+ memcpy(reinterpret_cast<char *>(mp) + *curOfs, img.constBits(), fullImageSizeBytes);
+ srcOffset = sy * bpl + sx * 4;
+ // bpl remains set to the original image's row stride
+ } else {
+ img = img.copy(sx, sy, w, h);
+ bpl = img.bytesPerLine();
+ Q_ASSERT(img.sizeInBytes() <= fullImageSizeBytes);
+ memcpy(reinterpret_cast<char *>(mp) + *curOfs, img.constBits(), img.sizeInBytes());
+ }
+ } else {
+ memcpy(reinterpret_cast<char *>(mp) + *curOfs, img.constBits(), fullImageSizeBytes);
+ }
+
+ [blitEnc copyFromBuffer: texD->d->stagingBuf[currentFrameSlot]
+ sourceOffset: *curOfs + srcOffset
+ sourceBytesPerRow: bpl
+ sourceBytesPerImage: 0
+ sourceSize: MTLSizeMake(w, h, 1)
+ toTexture: texD->d->tex
+ destinationSlice: layer
+ destinationLevel: level
+ destinationOrigin: MTLOriginMake(dp.x(), dp.y(), 0)
+ options: MTLBlitOptionNone];
+
+ *curOfs += aligned(fullImageSizeBytes, QRhiMetalData::TEXBUF_ALIGN);
+ } else if (!rawData.isEmpty() && isCompressedFormat(texD->m_format)) {
+ const QSize subresSize = q->sizeForMipLevel(level, texD->m_pixelSize);
+ const int subresw = subresSize.width();
+ const int subresh = subresSize.height();
+ int w, h;
+ if (subresDesc.sourceSize().isEmpty()) {
+ w = subresw;
+ h = subresh;
+ } else {
+ w = subresDesc.sourceSize().width();
+ h = subresDesc.sourceSize().height();
+ }
+
+ quint32 bpl = 0;
+ QSize blockDim;
+ compressedFormatInfo(texD->m_format, QSize(w, h), &bpl, nullptr, &blockDim);
+
+ const int dx = aligned(dp.x(), blockDim.width());
+ const int dy = aligned(dp.y(), blockDim.height());
+ if (dx + w != subresw)
+ w = aligned(w, blockDim.width());
+ if (dy + h != subresh)
+ h = aligned(h, blockDim.height());
+
+ memcpy(reinterpret_cast<char *>(mp) + *curOfs, rawData.constData(), rawData.size());
+
+ [blitEnc copyFromBuffer: texD->d->stagingBuf[currentFrameSlot]
+ sourceOffset: *curOfs
+ sourceBytesPerRow: bpl
+ sourceBytesPerImage: 0
+ sourceSize: MTLSizeMake(w, h, 1)
+ toTexture: texD->d->tex
+ destinationSlice: layer
+ destinationLevel: level
+ destinationOrigin: MTLOriginMake(dx, dy, 0)
+ options: MTLBlitOptionNone];
+
+ *curOfs += aligned(rawData.size(), QRhiMetalData::TEXBUF_ALIGN);
+ } else if (!rawData.isEmpty()) {
+ const QSize subresSize = q->sizeForMipLevel(level, texD->m_pixelSize);
+ const int subresw = subresSize.width();
+ const int subresh = subresSize.height();
+ int w, h;
+ if (subresDesc.sourceSize().isEmpty()) {
+ w = subresw;
+ h = subresh;
+ } else {
+ w = subresDesc.sourceSize().width();
+ h = subresDesc.sourceSize().height();
+ }
+
+ quint32 bpl = 0;
+ textureFormatInfo(texD->m_format, QSize(w, h), &bpl, nullptr);
+ memcpy(reinterpret_cast<char *>(mp) + *curOfs, rawData.constData(), rawData.size());
+
+ [blitEnc copyFromBuffer: texD->d->stagingBuf[currentFrameSlot]
+ sourceOffset: *curOfs
+ sourceBytesPerRow: bpl
+ sourceBytesPerImage: 0
+ sourceSize: MTLSizeMake(w, h, 1)
+ toTexture: texD->d->tex
+ destinationSlice: layer
+ destinationLevel: level
+ destinationOrigin: MTLOriginMake(dp.x(), dp.y(), 0)
+ options: MTLBlitOptionNone];
+
+ *curOfs += aligned(rawData.size(), QRhiMetalData::TEXBUF_ALIGN);
+ } else {
+ qWarning("Invalid texture upload for %p layer=%d mip=%d", texD, layer, level);
+ }
+}
+
+void QRhiMetal::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb);
+ QRhiResourceUpdateBatchPrivate *ud = QRhiResourceUpdateBatchPrivate::get(resourceUpdates);
+ QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
+
+ for (const QRhiResourceUpdateBatchPrivate::DynamicBufferUpdate &u : ud->dynamicBufferUpdates) {
+ QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, u.buf);
+ Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic);
+ for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
+ bufD->d->pendingUpdates[i].append(u);
+ }
+
+ for (const QRhiResourceUpdateBatchPrivate::StaticBufferUpload &u : ud->staticBufferUploads) {
+ QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, u.buf);
+ Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic);
+ Q_ASSERT(u.offset + u.data.size() <= bufD->m_size);
+ for (int i = 0, ie = bufD->m_type == QRhiBuffer::Immutable ? 1 : QMTL_FRAMES_IN_FLIGHT; i != ie; ++i)
+ bufD->d->pendingUpdates[i].append({ u.buf, u.offset, u.data.size(), u.data.constData() });
+ }
+
+ id<MTLBlitCommandEncoder> blitEnc = nil;
+ auto ensureBlit = [&blitEnc, cbD, this] {
+ if (!blitEnc) {
+ blitEnc = [cbD->d->cb blitCommandEncoder];
+ if (debugMarkers)
+ [blitEnc pushDebugGroup: @"Texture upload/copy"];
+ }
+ };
+
+ for (const QRhiResourceUpdateBatchPrivate::TextureOp &u : ud->textureOps) {
+ if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Upload) {
+ QMetalTexture *utexD = QRHI_RES(QMetalTexture, u.upload.tex);
+ qsizetype stagingSize = 0;
+ for (int layer = 0; layer < QRhi::MAX_LAYERS; ++layer) {
+ for (int level = 0; level < QRhi::MAX_LEVELS; ++level) {
+ for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.upload.subresDesc[layer][level]))
+ stagingSize += subresUploadByteSize(subresDesc);
+ }
+ }
+
+ ensureBlit();
+ Q_ASSERT(!utexD->d->stagingBuf[currentFrameSlot]);
+ utexD->d->stagingBuf[currentFrameSlot] = [d->dev newBufferWithLength: stagingSize
+ options: MTLResourceStorageModeShared];
+ QRHI_PROF_F(newTextureStagingArea(utexD, currentFrameSlot, stagingSize));
+
+ void *mp = [utexD->d->stagingBuf[currentFrameSlot] contents];
+ qsizetype curOfs = 0;
+ for (int layer = 0; layer < QRhi::MAX_LAYERS; ++layer) {
+ for (int level = 0; level < QRhi::MAX_LEVELS; ++level) {
+ for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.upload.subresDesc[layer][level]))
+ enqueueSubresUpload(utexD, mp, blitEnc, layer, level, subresDesc, &curOfs);
+ }
+ }
+
+ utexD->lastActiveFrameSlot = currentFrameSlot;
+
+ QRhiMetalData::DeferredReleaseEntry e;
+ e.type = QRhiMetalData::DeferredReleaseEntry::StagingBuffer;
+ e.lastActiveFrameSlot = currentFrameSlot;
+ e.stagingBuffer.buffer = utexD->d->stagingBuf[currentFrameSlot];
+ utexD->d->stagingBuf[currentFrameSlot] = nil;
+ d->releaseQueue.append(e);
+ QRHI_PROF_F(releaseTextureStagingArea(utexD, currentFrameSlot));
+ } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Copy) {
+ Q_ASSERT(u.copy.src && u.copy.dst);
+ QMetalTexture *srcD = QRHI_RES(QMetalTexture, u.copy.src);
+ QMetalTexture *dstD = QRHI_RES(QMetalTexture, u.copy.dst);
+ const QPoint dp = u.copy.desc.destinationTopLeft();
+ const QSize size = u.copy.desc.pixelSize().isEmpty() ? srcD->m_pixelSize : u.copy.desc.pixelSize();
+ const QPoint sp = u.copy.desc.sourceTopLeft();
+
+ ensureBlit();
+ [blitEnc copyFromTexture: srcD->d->tex
+ sourceSlice: u.copy.desc.sourceLayer()
+ sourceLevel: u.copy.desc.sourceLevel()
+ sourceOrigin: MTLOriginMake(sp.x(), sp.y(), 0)
+ sourceSize: MTLSizeMake(size.width(), size.height(), 1)
+ toTexture: dstD->d->tex
+ destinationSlice: u.copy.desc.destinationLayer()
+ destinationLevel: u.copy.desc.destinationLevel()
+ destinationOrigin: MTLOriginMake(dp.x(), dp.y(), 0)];
+
+ srcD->lastActiveFrameSlot = dstD->lastActiveFrameSlot = currentFrameSlot;
+ } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Read) {
+ QRhiMetalData::ActiveReadback aRb;
+ aRb.activeFrameSlot = currentFrameSlot;
+ aRb.desc = u.read.rb;
+ aRb.result = u.read.result;
+
+ QMetalTexture *texD = QRHI_RES(QMetalTexture, u.read.rb.texture());
+ QMetalSwapChain *swapChainD = nullptr;
+ id<MTLTexture> src;
+ QSize srcSize;
+ if (texD) {
+ if (texD->samples > 1) {
+ qWarning("Multisample texture cannot be read back");
+ continue;
+ }
+ aRb.pixelSize = u.read.rb.level() > 0 ? q->sizeForMipLevel(u.read.rb.level(), texD->m_pixelSize)
+ : texD->m_pixelSize;
+ aRb.format = texD->m_format;
+ src = texD->d->tex;
+ srcSize = texD->m_pixelSize;
+ texD->lastActiveFrameSlot = currentFrameSlot;
+ } else {
+ Q_ASSERT(currentSwapChain);
+ swapChainD = QRHI_RES(QMetalSwapChain, currentSwapChain);
+ aRb.pixelSize = swapChainD->pixelSize;
+ aRb.format = swapChainD->d->rhiColorFormat;
+ // Multisample swapchains need nothing special since resolving
+ // happens when ending a renderpass.
+ const QMetalRenderTargetData::ColorAtt &colorAtt(swapChainD->rtWrapper.d->fb.colorAtt[0]);
+ src = colorAtt.resolveTex ? colorAtt.resolveTex : colorAtt.tex;
+ srcSize = swapChainD->rtWrapper.d->pixelSize;
+ }
+
+ quint32 bpl = 0;
+ textureFormatInfo(aRb.format, aRb.pixelSize, &bpl, &aRb.bufSize);
+ aRb.buf = [d->dev newBufferWithLength: aRb.bufSize options: MTLResourceStorageModeShared];
+
+ QRHI_PROF_F(newReadbackBuffer(quint64(quintptr(aRb.buf)),
+ texD ? static_cast<QRhiResource *>(texD) : static_cast<QRhiResource *>(swapChainD),
+ aRb.bufSize));
+
+ ensureBlit();
+ [blitEnc copyFromTexture: src
+ sourceSlice: u.read.rb.layer()
+ sourceLevel: u.read.rb.level()
+ sourceOrigin: MTLOriginMake(0, 0, 0)
+ sourceSize: MTLSizeMake(srcSize.width(), srcSize.height(), 1)
+ toBuffer: aRb.buf
+ destinationOffset: 0
+ destinationBytesPerRow: bpl
+ destinationBytesPerImage: 0
+ options: MTLBlitOptionNone];
+
+ d->activeReadbacks.append(aRb);
+ } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::MipGen) {
+ QMetalTexture *utexD = QRHI_RES(QMetalTexture, u.mipgen.tex);
+ ensureBlit();
+ [blitEnc generateMipmapsForTexture: utexD->d->tex];
+ utexD->lastActiveFrameSlot = currentFrameSlot;
+ }
+ }
+
+ if (blitEnc) {
+ if (debugMarkers)
+ [blitEnc popDebugGroup];
+ [blitEnc endEncoding];
+ }
+
+ ud->free();
+}
+
+void QRhiMetal::executeBufferHostWritesForCurrentFrame(QMetalBuffer *bufD)
+{
+ const int idx = bufD->m_type == QRhiBuffer::Immutable ? 0 : currentFrameSlot;
+ QVector<QRhiResourceUpdateBatchPrivate::DynamicBufferUpdate> &updates(bufD->d->pendingUpdates[idx]);
+ if (updates.isEmpty())
+ return;
+
+ void *p = [bufD->d->buf[idx] contents];
+ int changeBegin = -1;
+ int changeEnd = -1;
+ for (const QRhiResourceUpdateBatchPrivate::DynamicBufferUpdate &u : updates) {
+ Q_ASSERT(bufD == QRHI_RES(QMetalBuffer, u.buf));
+ memcpy(static_cast<char *>(p) + u.offset, u.data.constData(), u.data.size());
+ if (changeBegin == -1 || u.offset < changeBegin)
+ changeBegin = u.offset;
+ if (changeEnd == -1 || u.offset + u.data.size() > changeEnd)
+ changeEnd = u.offset + u.data.size();
+ }
+ if (changeBegin >= 0 && bufD->d->managed)
+ [bufD->d->buf[idx] didModifyRange: NSMakeRange(changeBegin, changeEnd - changeBegin)];
+
+ updates.clear();
+}
+
+void QRhiMetal::resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ Q_ASSERT(inFrame && !inPass);
+
+ enqueueResourceUpdates(cb, resourceUpdates);
+}
+
+void QRhiMetal::beginPass(QRhiCommandBuffer *cb,
+ QRhiRenderTarget *rt,
+ const QColor &colorClearValue,
+ const QRhiDepthStencilClearValue &depthStencilClearValue,
+ QRhiResourceUpdateBatch *resourceUpdates)
+{
+ Q_ASSERT(inFrame && !inPass);
+
+ if (resourceUpdates)
+ enqueueResourceUpdates(cb, resourceUpdates);
+
+ QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb);
+
+ QMetalRenderTargetData *rtD = nullptr;
+ switch (rt->resourceType()) {
+ case QRhiResource::RenderTarget:
+ rtD = QRHI_RES(QMetalReferenceRenderTarget, rt)->d;
+ cbD->d->currentPassRpDesc = d->createDefaultRenderPass(rtD->dsAttCount, colorClearValue, depthStencilClearValue, rtD->colorAttCount);
+ if (rtD->colorAttCount) {
+ QMetalRenderTargetData::ColorAtt &color0(rtD->fb.colorAtt[0]);
+ if (color0.needsDrawableForTex || color0.needsDrawableForResolveTex) {
+ Q_ASSERT(currentSwapChain);
+ QMetalSwapChain *swapChainD = QRHI_RES(QMetalSwapChain, currentSwapChain);
+ swapChainD->d->curDrawable = [swapChainD->d->layer nextDrawable];
+ if (!swapChainD->d->curDrawable) {
+ qWarning("No drawable");
+ return;
+ }
+ id<MTLTexture> scTex = swapChainD->d->curDrawable.texture;
+ if (color0.needsDrawableForTex) {
+ color0.tex = scTex;
+ color0.needsDrawableForTex = false;
+ } else {
+ color0.resolveTex = scTex;
+ color0.needsDrawableForResolveTex = false;
+ }
+ }
+ }
+ break;
+ case QRhiResource::TextureRenderTarget:
+ {
+ QMetalTextureRenderTarget *rtTex = QRHI_RES(QMetalTextureRenderTarget, rt);
+ rtD = rtTex->d;
+ cbD->d->currentPassRpDesc = d->createDefaultRenderPass(rtD->dsAttCount, colorClearValue, depthStencilClearValue, rtD->colorAttCount);
+ if (rtTex->m_flags.testFlag(QRhiTextureRenderTarget::PreserveColorContents)) {
+ for (int i = 0; i < rtD->colorAttCount; ++i)
+ cbD->d->currentPassRpDesc.colorAttachments[i].loadAction = MTLLoadActionLoad;
+ }
+ if (rtD->dsAttCount && rtTex->m_flags.testFlag(QRhiTextureRenderTarget::PreserveDepthStencilContents)) {
+ cbD->d->currentPassRpDesc.depthAttachment.loadAction = MTLLoadActionLoad;
+ cbD->d->currentPassRpDesc.stencilAttachment.loadAction = MTLLoadActionLoad;
+ }
+ const QVector<QRhiColorAttachment> colorAttachments = rtTex->m_desc.colorAttachments();
+ for (const QRhiColorAttachment &colorAttachment : colorAttachments) {
+ if (colorAttachment.texture())
+ QRHI_RES(QMetalTexture, colorAttachment.texture())->lastActiveFrameSlot = currentFrameSlot;
+ else if (colorAttachment.renderBuffer())
+ QRHI_RES(QMetalRenderBuffer, colorAttachment.renderBuffer())->lastActiveFrameSlot = currentFrameSlot;
+ if (colorAttachment.resolveTexture())
+ QRHI_RES(QMetalTexture, colorAttachment.resolveTexture())->lastActiveFrameSlot = currentFrameSlot;
+ }
+ if (rtTex->m_desc.depthStencilBuffer())
+ QRHI_RES(QMetalRenderBuffer, rtTex->m_desc.depthStencilBuffer())->lastActiveFrameSlot = currentFrameSlot;
+ if (rtTex->m_desc.depthTexture())
+ QRHI_RES(QMetalTexture, rtTex->m_desc.depthTexture())->lastActiveFrameSlot = currentFrameSlot;
+ }
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+
+ for (int i = 0; i < rtD->colorAttCount; ++i) {
+ cbD->d->currentPassRpDesc.colorAttachments[i].texture = rtD->fb.colorAtt[i].tex;
+ cbD->d->currentPassRpDesc.colorAttachments[i].slice = rtD->fb.colorAtt[i].layer;
+ cbD->d->currentPassRpDesc.colorAttachments[i].level = rtD->fb.colorAtt[i].level;
+ if (rtD->fb.colorAtt[i].resolveTex) {
+ cbD->d->currentPassRpDesc.colorAttachments[i].storeAction = MTLStoreActionMultisampleResolve;
+ cbD->d->currentPassRpDesc.colorAttachments[i].resolveTexture = rtD->fb.colorAtt[i].resolveTex;
+ cbD->d->currentPassRpDesc.colorAttachments[i].resolveSlice = rtD->fb.colorAtt[i].resolveLayer;
+ cbD->d->currentPassRpDesc.colorAttachments[i].resolveLevel = rtD->fb.colorAtt[i].resolveLevel;
+ }
+ }
+
+ if (rtD->dsAttCount) {
+ Q_ASSERT(rtD->fb.dsTex);
+ cbD->d->currentPassRpDesc.depthAttachment.texture = rtD->fb.dsTex;
+ cbD->d->currentPassRpDesc.stencilAttachment.texture = rtD->fb.hasStencil ? rtD->fb.dsTex : nil;
+ if (rtD->fb.depthNeedsStore) // Depth/Stencil is set to DontCare by default, override if needed
+ cbD->d->currentPassRpDesc.depthAttachment.storeAction = MTLStoreActionStore;
+ }
+
+ cbD->d->currentPassEncoder = [cbD->d->cb renderCommandEncoderWithDescriptor: cbD->d->currentPassRpDesc];
+
+ cbD->resetPerPassState();
+
+ cbD->currentTarget = rt;
+ inPass = true;
+}
+
+void QRhiMetal::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ Q_ASSERT(inPass);
+ inPass = false;
+
+ QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb);
+ [cbD->d->currentPassEncoder endEncoding];
+
+ cbD->currentTarget = nullptr;
+
+ if (resourceUpdates)
+ enqueueResourceUpdates(cb, resourceUpdates);
+}
+
+static void qrhimtl_releaseBuffer(const QRhiMetalData::DeferredReleaseEntry &e)
+{
+ for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
+ [e.buffer.buffers[i] release];
+}
+
+static void qrhimtl_releaseRenderBuffer(const QRhiMetalData::DeferredReleaseEntry &e)
+{
+ [e.renderbuffer.texture release];
+}
+
+static void qrhimtl_releaseTexture(const QRhiMetalData::DeferredReleaseEntry &e)
+{
+ [e.texture.texture release];
+ for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
+ [e.texture.stagingBuffers[i] release];
+}
+
+static void qrhimtl_releaseSampler(const QRhiMetalData::DeferredReleaseEntry &e)
+{
+ [e.sampler.samplerState release];
+}
+
+void QRhiMetal::executeDeferredReleases(bool forced)
+{
+ for (int i = d->releaseQueue.count() - 1; i >= 0; --i) {
+ const QRhiMetalData::DeferredReleaseEntry &e(d->releaseQueue[i]);
+ if (forced || currentFrameSlot == e.lastActiveFrameSlot || e.lastActiveFrameSlot < 0) {
+ switch (e.type) {
+ case QRhiMetalData::DeferredReleaseEntry::Buffer:
+ qrhimtl_releaseBuffer(e);
+ break;
+ case QRhiMetalData::DeferredReleaseEntry::RenderBuffer:
+ qrhimtl_releaseRenderBuffer(e);
+ break;
+ case QRhiMetalData::DeferredReleaseEntry::Texture:
+ qrhimtl_releaseTexture(e);
+ break;
+ case QRhiMetalData::DeferredReleaseEntry::Sampler:
+ qrhimtl_releaseSampler(e);
+ break;
+ case QRhiMetalData::DeferredReleaseEntry::StagingBuffer:
+ [e.stagingBuffer.buffer release];
+ break;
+ default:
+ break;
+ }
+ d->releaseQueue.removeAt(i);
+ }
+ }
+}
+
+void QRhiMetal::finishActiveReadbacks(bool forced)
+{
+ QVarLengthArray<std::function<void()>, 4> completedCallbacks;
+ QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
+
+ for (int i = d->activeReadbacks.count() - 1; i >= 0; --i) {
+ const QRhiMetalData::ActiveReadback &aRb(d->activeReadbacks[i]);
+ if (forced || currentFrameSlot == aRb.activeFrameSlot || aRb.activeFrameSlot < 0) {
+ aRb.result->format = aRb.format;
+ aRb.result->pixelSize = aRb.pixelSize;
+ aRb.result->data.resize(aRb.bufSize);
+ void *p = [aRb.buf contents];
+ memcpy(aRb.result->data.data(), p, aRb.bufSize);
+ [aRb.buf release];
+
+ QRHI_PROF_F(releaseReadbackBuffer(quint64(quintptr(aRb.buf))));
+
+ if (aRb.result->completed)
+ completedCallbacks.append(aRb.result->completed);
+
+ d->activeReadbacks.removeAt(i);
+ }
+ }
+
+ for (auto f : completedCallbacks)
+ f();
+}
+
+QMetalBuffer::QMetalBuffer(QRhiImplementation *rhi, Type type, UsageFlags usage, int size)
+ : QRhiBuffer(rhi, type, usage, size),
+ d(new QMetalBufferData)
+{
+ for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
+ d->buf[i] = nil;
+}
+
+QMetalBuffer::~QMetalBuffer()
+{
+ release();
+ delete d;
+}
+
+void QMetalBuffer::release()
+{
+ if (!d->buf[0])
+ return;
+
+ QRhiMetalData::DeferredReleaseEntry e;
+ e.type = QRhiMetalData::DeferredReleaseEntry::Buffer;
+ e.lastActiveFrameSlot = lastActiveFrameSlot;
+
+ for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
+ e.buffer.buffers[i] = d->buf[i];
+ d->buf[i] = nil;
+ d->pendingUpdates[i].clear();
+ }
+
+ QRHI_RES_RHI(QRhiMetal);
+ rhiD->d->releaseQueue.append(e);
+ QRHI_PROF;
+ QRHI_PROF_F(releaseBuffer(this));
+ rhiD->unregisterResource(this);
+}
+
+bool QMetalBuffer::build()
+{
+ if (d->buf[0])
+ release();
+
+ const int nonZeroSize = m_size <= 0 ? 256 : m_size;
+ const int roundedSize = m_usage.testFlag(QRhiBuffer::UniformBuffer) ? aligned(nonZeroSize, 256) : nonZeroSize;
+
+ d->managed = false;
+ MTLResourceOptions opts = MTLResourceStorageModeShared;
+#ifdef Q_OS_MACOS
+ if (m_type != Dynamic) {
+ opts = MTLResourceStorageModeManaged;
+ d->managed = true;
+ }
+#endif
+
+ QRHI_RES_RHI(QRhiMetal);
+ for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
+ // Immutable only has buf[0] and pendingUpdates[0] in use.
+ // Static and Dynamic use all.
+ if (i == 0 || m_type != Immutable) {
+ d->buf[i] = [rhiD->d->dev newBufferWithLength: roundedSize options: opts];
+ d->pendingUpdates[i].reserve(16);
+ if (!m_objectName.isEmpty()) {
+ if (m_type == Immutable) {
+ d->buf[i].label = [NSString stringWithUTF8String: m_objectName.constData()];
+ } else {
+ const QByteArray name = m_objectName + '/' + QByteArray::number(i);
+ d->buf[i].label = [NSString stringWithUTF8String: name.constData()];
+ }
+ }
+ }
+ }
+
+ QRHI_PROF;
+ QRHI_PROF_F(newBuffer(this, roundedSize, m_type == Immutable ? 1 : QMTL_FRAMES_IN_FLIGHT, 0));
+
+ lastActiveFrameSlot = -1;
+ generation += 1;
+ rhiD->registerResource(this);
+ return true;
+}
+
+QMetalRenderBuffer::QMetalRenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize,
+ int sampleCount, QRhiRenderBuffer::Flags flags)
+ : QRhiRenderBuffer(rhi, type, pixelSize, sampleCount, flags),
+ d(new QMetalRenderBufferData)
+{
+}
+
+QMetalRenderBuffer::~QMetalRenderBuffer()
+{
+ release();
+ delete d;
+}
+
+void QMetalRenderBuffer::release()
+{
+ if (!d->tex)
+ return;
+
+ QRhiMetalData::DeferredReleaseEntry e;
+ e.type = QRhiMetalData::DeferredReleaseEntry::RenderBuffer;
+ e.lastActiveFrameSlot = lastActiveFrameSlot;
+
+ e.renderbuffer.texture = d->tex;
+ d->tex = nil;
+
+ QRHI_RES_RHI(QRhiMetal);
+ rhiD->d->releaseQueue.append(e);
+ QRHI_PROF;
+ QRHI_PROF_F(releaseRenderBuffer(this));
+ rhiD->unregisterResource(this);
+}
+
+bool QMetalRenderBuffer::build()
+{
+ if (d->tex)
+ release();
+
+ if (m_pixelSize.isEmpty())
+ return false;
+
+ QRHI_RES_RHI(QRhiMetal);
+ samples = rhiD->effectiveSampleCount(m_sampleCount);
+
+ MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init];
+ desc.textureType = samples > 1 ? MTLTextureType2DMultisample : MTLTextureType2D;
+ desc.width = m_pixelSize.width();
+ desc.height = m_pixelSize.height();
+ if (samples > 1)
+ desc.sampleCount = samples;
+ desc.resourceOptions = MTLResourceStorageModePrivate;
+ desc.usage = MTLTextureUsageRenderTarget;
+
+ bool transientBacking = false;
+ switch (m_type) {
+ case DepthStencil:
+#ifdef Q_OS_MACOS
+ desc.storageMode = MTLStorageModePrivate;
+#else
+ desc.storageMode = MTLResourceStorageModeMemoryless;
+ transientBacking = true;
+#endif
+ d->format = rhiD->d->dev.depth24Stencil8PixelFormatSupported
+ ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8;
+ desc.pixelFormat = d->format;
+ break;
+ case Color:
+ desc.storageMode = MTLStorageModePrivate;
+ d->format = MTLPixelFormatRGBA8Unorm;
+ desc.pixelFormat = d->format;
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+
+ d->tex = [rhiD->d->dev newTextureWithDescriptor: desc];
+ [desc release];
+
+ if (!m_objectName.isEmpty())
+ d->tex.label = [NSString stringWithUTF8String: m_objectName.constData()];
+
+ QRHI_PROF;
+ QRHI_PROF_F(newRenderBuffer(this, transientBacking, false, samples));
+
+ lastActiveFrameSlot = -1;
+ generation += 1;
+ rhiD->registerResource(this);
+ return true;
+}
+
+QRhiTexture::Format QMetalRenderBuffer::backingFormat() const
+{
+ return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat;
+}
+
+QMetalTexture::QMetalTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize,
+ int sampleCount, Flags flags)
+ : QRhiTexture(rhi, format, pixelSize, sampleCount, flags),
+ d(new QMetalTextureData)
+{
+ for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
+ d->stagingBuf[i] = nil;
+}
+
+QMetalTexture::~QMetalTexture()
+{
+ release();
+ delete d;
+}
+
+void QMetalTexture::release()
+{
+ if (!d->tex)
+ return;
+
+ QRhiMetalData::DeferredReleaseEntry e;
+ e.type = QRhiMetalData::DeferredReleaseEntry::Texture;
+ e.lastActiveFrameSlot = lastActiveFrameSlot;
+
+ e.texture.texture = d->owns ? d->tex : nil;
+ d->tex = nil;
+ nativeHandlesStruct.texture = nullptr;
+
+ for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
+ e.texture.stagingBuffers[i] = d->stagingBuf[i];
+ d->stagingBuf[i] = nil;
+ }
+
+ QRHI_RES_RHI(QRhiMetal);
+ rhiD->d->releaseQueue.append(e);
+ QRHI_PROF;
+ QRHI_PROF_F(releaseTexture(this));
+ rhiD->unregisterResource(this);
+}
+
+static inline MTLPixelFormat toMetalTextureFormat(QRhiTexture::Format format, QRhiTexture::Flags flags)
+{
+ const bool srgb = flags.testFlag(QRhiTexture::sRGB);
+ switch (format) {
+ case QRhiTexture::RGBA8:
+ return srgb ? MTLPixelFormatRGBA8Unorm_sRGB : MTLPixelFormatRGBA8Unorm;
+ case QRhiTexture::BGRA8:
+ return srgb ? MTLPixelFormatBGRA8Unorm_sRGB : MTLPixelFormatBGRA8Unorm;
+ case QRhiTexture::R8:
+#ifdef Q_OS_MACOS
+ return MTLPixelFormatR8Unorm;
+#else
+ return srgb ? MTLPixelFormatR8Unorm_sRGB : MTLPixelFormatR8Unorm;
+#endif
+ case QRhiTexture::R16:
+ return MTLPixelFormatR16Unorm;
+ case QRhiTexture::RED_OR_ALPHA8:
+ return MTLPixelFormatR8Unorm;
+
+ case QRhiTexture::RGBA16F:
+ return MTLPixelFormatRGBA16Float;
+ case QRhiTexture::RGBA32F:
+ return MTLPixelFormatRGBA32Float;
+
+ case QRhiTexture::D16:
+#ifdef Q_OS_MACOS
+ return MTLPixelFormatDepth16Unorm;
+#else
+ return MTLPixelFormatDepth32Float;
+#endif
+ case QRhiTexture::D32F:
+ return MTLPixelFormatDepth32Float;
+
+#ifdef Q_OS_MACOS
+ case QRhiTexture::BC1:
+ return srgb ? MTLPixelFormatBC1_RGBA_sRGB : MTLPixelFormatBC1_RGBA;
+ case QRhiTexture::BC2:
+ return srgb ? MTLPixelFormatBC2_RGBA_sRGB : MTLPixelFormatBC2_RGBA;
+ case QRhiTexture::BC3:
+ return srgb ? MTLPixelFormatBC3_RGBA_sRGB : MTLPixelFormatBC3_RGBA;
+ case QRhiTexture::BC4:
+ return MTLPixelFormatBC4_RUnorm;
+ case QRhiTexture::BC5:
+ qWarning("QRhiMetal does not support BC5");
+ return MTLPixelFormatRGBA8Unorm;
+ case QRhiTexture::BC6H:
+ return MTLPixelFormatBC6H_RGBUfloat;
+ case QRhiTexture::BC7:
+ return srgb ? MTLPixelFormatBC7_RGBAUnorm_sRGB : MTLPixelFormatBC7_RGBAUnorm;
+#else
+ case QRhiTexture::BC1:
+ case QRhiTexture::BC2:
+ case QRhiTexture::BC3:
+ case QRhiTexture::BC4:
+ case QRhiTexture::BC5:
+ case QRhiTexture::BC6H:
+ case QRhiTexture::BC7:
+ qWarning("QRhiMetal: BCx compression not supported on this platform");
+ return MTLPixelFormatRGBA8Unorm;
+#endif
+
+#ifndef Q_OS_MACOS
+ case QRhiTexture::ETC2_RGB8:
+ return srgb ? MTLPixelFormatETC2_RGB8_sRGB : MTLPixelFormatETC2_RGB8;
+ case QRhiTexture::ETC2_RGB8A1:
+ return srgb ? MTLPixelFormatETC2_RGB8A1_sRGB : MTLPixelFormatETC2_RGB8A1;
+ case QRhiTexture::ETC2_RGBA8:
+ return srgb ? MTLPixelFormatEAC_RGBA8_sRGB : MTLPixelFormatEAC_RGBA8;
+
+ case QRhiTexture::ASTC_4x4:
+ return srgb ? MTLPixelFormatASTC_4x4_sRGB : MTLPixelFormatASTC_4x4_LDR;
+ case QRhiTexture::ASTC_5x4:
+ return srgb ? MTLPixelFormatASTC_5x4_sRGB : MTLPixelFormatASTC_5x4_LDR;
+ case QRhiTexture::ASTC_5x5:
+ return srgb ? MTLPixelFormatASTC_5x5_sRGB : MTLPixelFormatASTC_5x5_LDR;
+ case QRhiTexture::ASTC_6x5:
+ return srgb ? MTLPixelFormatASTC_6x5_sRGB : MTLPixelFormatASTC_6x5_LDR;
+ case QRhiTexture::ASTC_6x6:
+ return srgb ? MTLPixelFormatASTC_6x6_sRGB : MTLPixelFormatASTC_6x6_LDR;
+ case QRhiTexture::ASTC_8x5:
+ return srgb ? MTLPixelFormatASTC_8x5_sRGB : MTLPixelFormatASTC_8x5_LDR;
+ case QRhiTexture::ASTC_8x6:
+ return srgb ? MTLPixelFormatASTC_8x6_sRGB : MTLPixelFormatASTC_8x6_LDR;
+ case QRhiTexture::ASTC_8x8:
+ return srgb ? MTLPixelFormatASTC_8x8_sRGB : MTLPixelFormatASTC_8x8_LDR;
+ case QRhiTexture::ASTC_10x5:
+ return srgb ? MTLPixelFormatASTC_10x5_sRGB : MTLPixelFormatASTC_10x5_LDR;
+ case QRhiTexture::ASTC_10x6:
+ return srgb ? MTLPixelFormatASTC_10x6_sRGB : MTLPixelFormatASTC_10x6_LDR;
+ case QRhiTexture::ASTC_10x8:
+ return srgb ? MTLPixelFormatASTC_10x8_sRGB : MTLPixelFormatASTC_10x8_LDR;
+ case QRhiTexture::ASTC_10x10:
+ return srgb ? MTLPixelFormatASTC_10x10_sRGB : MTLPixelFormatASTC_10x10_LDR;
+ case QRhiTexture::ASTC_12x10:
+ return srgb ? MTLPixelFormatASTC_12x10_sRGB : MTLPixelFormatASTC_12x10_LDR;
+ case QRhiTexture::ASTC_12x12:
+ return srgb ? MTLPixelFormatASTC_12x12_sRGB : MTLPixelFormatASTC_12x12_LDR;
+#else
+ case QRhiTexture::ETC2_RGB8:
+ case QRhiTexture::ETC2_RGB8A1:
+ case QRhiTexture::ETC2_RGBA8:
+ qWarning("QRhiMetal: ETC2 compression not supported on this platform");
+ return MTLPixelFormatRGBA8Unorm;
+
+ case QRhiTexture::ASTC_4x4:
+ case QRhiTexture::ASTC_5x4:
+ case QRhiTexture::ASTC_5x5:
+ case QRhiTexture::ASTC_6x5:
+ case QRhiTexture::ASTC_6x6:
+ case QRhiTexture::ASTC_8x5:
+ case QRhiTexture::ASTC_8x6:
+ case QRhiTexture::ASTC_8x8:
+ case QRhiTexture::ASTC_10x5:
+ case QRhiTexture::ASTC_10x6:
+ case QRhiTexture::ASTC_10x8:
+ case QRhiTexture::ASTC_10x10:
+ case QRhiTexture::ASTC_12x10:
+ case QRhiTexture::ASTC_12x12:
+ qWarning("QRhiMetal: ASTC compression not supported on this platform");
+ return MTLPixelFormatRGBA8Unorm;
+#endif
+
+ default:
+ Q_UNREACHABLE();
+ return MTLPixelFormatRGBA8Unorm;
+ }
+}
+
+bool QMetalTexture::prepareBuild(QSize *adjustedSize)
+{
+ if (d->tex)
+ release();
+
+ const QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize;
+ const bool isCube = m_flags.testFlag(CubeMap);
+ const bool hasMipMaps = m_flags.testFlag(MipMapped);
+
+ QRHI_RES_RHI(QRhiMetal);
+ d->format = toMetalTextureFormat(m_format, m_flags);
+ mipLevelCount = hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1;
+ samples = rhiD->effectiveSampleCount(m_sampleCount);
+ if (samples > 1) {
+ if (isCube) {
+ qWarning("Cubemap texture cannot be multisample");
+ return false;
+ }
+ if (hasMipMaps) {
+ qWarning("Multisample texture cannot have mipmaps");
+ return false;
+ }
+ }
+
+ if (adjustedSize)
+ *adjustedSize = size;
+
+ return true;
+}
+
+bool QMetalTexture::build()
+{
+ QSize size;
+ if (!prepareBuild(&size))
+ return false;
+
+ MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init];
+
+ const bool isCube = m_flags.testFlag(CubeMap);
+ if (isCube)
+ desc.textureType = MTLTextureTypeCube;
+ else
+ desc.textureType = samples > 1 ? MTLTextureType2DMultisample : MTLTextureType2D;
+ desc.pixelFormat = d->format;
+ desc.width = size.width();
+ desc.height = size.height();
+ desc.mipmapLevelCount = mipLevelCount;
+ if (samples > 1)
+ desc.sampleCount = samples;
+ desc.resourceOptions = MTLResourceStorageModePrivate;
+ desc.storageMode = MTLStorageModePrivate;
+ desc.usage = MTLTextureUsageShaderRead;
+ if (m_flags.testFlag(RenderTarget))
+ desc.usage |= MTLTextureUsageRenderTarget;
+
+ QRHI_RES_RHI(QRhiMetal);
+ d->tex = [rhiD->d->dev newTextureWithDescriptor: desc];
+ [desc release];
+
+ if (!m_objectName.isEmpty())
+ d->tex.label = [NSString stringWithUTF8String: m_objectName.constData()];
+
+ d->owns = true;
+ nativeHandlesStruct.texture = d->tex;
+
+ QRHI_PROF;
+ QRHI_PROF_F(newTexture(this, true, mipLevelCount, isCube ? 6 : 1, samples));
+
+ lastActiveFrameSlot = -1;
+ generation += 1;
+ rhiD->registerResource(this);
+ return true;
+}
+
+bool QMetalTexture::buildFrom(const QRhiNativeHandles *src)
+{
+ const QRhiMetalTextureNativeHandles *h = static_cast<const QRhiMetalTextureNativeHandles *>(src);
+ if (!h || !h->texture)
+ return false;
+
+ if (!prepareBuild())
+ return false;
+
+ d->tex = (id<MTLTexture>) h->texture;
+
+ d->owns = false;
+ nativeHandlesStruct.texture = d->tex;
+
+ QRHI_PROF;
+ QRHI_PROF_F(newTexture(this, false, mipLevelCount, m_flags.testFlag(CubeMap) ? 6 : 1, samples));
+
+ lastActiveFrameSlot = -1;
+ generation += 1;
+ QRHI_RES_RHI(QRhiMetal);
+ rhiD->registerResource(this);
+ return true;
+}
+
+const QRhiNativeHandles *QMetalTexture::nativeHandles()
+{
+ return &nativeHandlesStruct;
+}
+
+QMetalSampler::QMetalSampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode,
+ AddressMode u, AddressMode v)
+ : QRhiSampler(rhi, magFilter, minFilter, mipmapMode, u, v),
+ d(new QMetalSamplerData)
+{
+}
+
+QMetalSampler::~QMetalSampler()
+{
+ release();
+ delete d;
+}
+
+void QMetalSampler::release()
+{
+ if (!d->samplerState)
+ return;
+
+ QRhiMetalData::DeferredReleaseEntry e;
+ e.type = QRhiMetalData::DeferredReleaseEntry::Sampler;
+ e.lastActiveFrameSlot = lastActiveFrameSlot;
+
+ e.sampler.samplerState = d->samplerState;
+ d->samplerState = nil;
+
+ QRHI_RES_RHI(QRhiMetal);
+ rhiD->d->releaseQueue.append(e);
+ rhiD->unregisterResource(this);
+}
+
+static inline MTLSamplerMinMagFilter toMetalFilter(QRhiSampler::Filter f)
+{
+ switch (f) {
+ case QRhiSampler::Nearest:
+ return MTLSamplerMinMagFilterNearest;
+ case QRhiSampler::Linear:
+ return MTLSamplerMinMagFilterLinear;
+ default:
+ Q_UNREACHABLE();
+ return MTLSamplerMinMagFilterNearest;
+ }
+}
+
+static inline MTLSamplerMipFilter toMetalMipmapMode(QRhiSampler::Filter f)
+{
+ switch (f) {
+ case QRhiSampler::None:
+ return MTLSamplerMipFilterNotMipmapped;
+ case QRhiSampler::Nearest:
+ return MTLSamplerMipFilterNearest;
+ case QRhiSampler::Linear:
+ return MTLSamplerMipFilterLinear;
+ default:
+ Q_UNREACHABLE();
+ return MTLSamplerMipFilterNotMipmapped;
+ }
+}
+
+static inline MTLSamplerAddressMode toMetalAddressMode(QRhiSampler::AddressMode m)
+{
+ switch (m) {
+ case QRhiSampler::Repeat:
+ return MTLSamplerAddressModeRepeat;
+ case QRhiSampler::ClampToEdge:
+ return MTLSamplerAddressModeClampToEdge;
+ case QRhiSampler::Border:
+ return MTLSamplerAddressModeClampToBorderColor;
+ case QRhiSampler::Mirror:
+ return MTLSamplerAddressModeMirrorRepeat;
+ case QRhiSampler::MirrorOnce:
+ return MTLSamplerAddressModeMirrorClampToEdge;
+ default:
+ Q_UNREACHABLE();
+ return MTLSamplerAddressModeClampToEdge;
+ }
+}
+
+static inline MTLCompareFunction toMetalTextureCompareFunction(QRhiSampler::CompareOp op)
+{
+ switch (op) {
+ case QRhiSampler::Never:
+ return MTLCompareFunctionNever;
+ case QRhiSampler::Less:
+ return MTLCompareFunctionLess;
+ case QRhiSampler::Equal:
+ return MTLCompareFunctionEqual;
+ case QRhiSampler::LessOrEqual:
+ return MTLCompareFunctionLessEqual;
+ case QRhiSampler::Greater:
+ return MTLCompareFunctionGreater;
+ case QRhiSampler::NotEqual:
+ return MTLCompareFunctionNotEqual;
+ case QRhiSampler::GreaterOrEqual:
+ return MTLCompareFunctionGreaterEqual;
+ case QRhiSampler::Always:
+ return MTLCompareFunctionAlways;
+ default:
+ Q_UNREACHABLE();
+ return MTLCompareFunctionNever;
+ }
+}
+
+bool QMetalSampler::build()
+{
+ if (d->samplerState)
+ release();
+
+ MTLSamplerDescriptor *desc = [[MTLSamplerDescriptor alloc] init];
+ desc.minFilter = toMetalFilter(m_minFilter);
+ desc.magFilter = toMetalFilter(m_magFilter);
+ desc.mipFilter = toMetalMipmapMode(m_mipmapMode);
+ desc.sAddressMode = toMetalAddressMode(m_addressU);
+ desc.tAddressMode = toMetalAddressMode(m_addressV);
+ desc.rAddressMode = toMetalAddressMode(m_addressW);
+ desc.compareFunction = toMetalTextureCompareFunction(m_compareOp);
+
+ QRHI_RES_RHI(QRhiMetal);
+ d->samplerState = [rhiD->d->dev newSamplerStateWithDescriptor: desc];
+ [desc release];
+
+ lastActiveFrameSlot = -1;
+ generation += 1;
+ rhiD->registerResource(this);
+ return true;
+}
+
+// dummy, no Vulkan-style RenderPass+Framebuffer concept here.
+// We do have MTLRenderPassDescriptor of course, but it will be created on the fly for each pass.
+QMetalRenderPassDescriptor::QMetalRenderPassDescriptor(QRhiImplementation *rhi)
+ : QRhiRenderPassDescriptor(rhi)
+{
+}
+
+QMetalRenderPassDescriptor::~QMetalRenderPassDescriptor()
+{
+ release();
+}
+
+void QMetalRenderPassDescriptor::release()
+{
+ // nothing to do here
+}
+
+QMetalReferenceRenderTarget::QMetalReferenceRenderTarget(QRhiImplementation *rhi)
+ : QRhiRenderTarget(rhi),
+ d(new QMetalRenderTargetData)
+{
+}
+
+QMetalReferenceRenderTarget::~QMetalReferenceRenderTarget()
+{
+ release();
+ delete d;
+}
+
+void QMetalReferenceRenderTarget::release()
+{
+ // nothing to do here
+}
+
+QSize QMetalReferenceRenderTarget::pixelSize() const
+{
+ return d->pixelSize;
+}
+
+float QMetalReferenceRenderTarget::devicePixelRatio() const
+{
+ return d->dpr;
+}
+
+int QMetalReferenceRenderTarget::sampleCount() const
+{
+ return d->sampleCount;
+}
+
+QMetalTextureRenderTarget::QMetalTextureRenderTarget(QRhiImplementation *rhi,
+ const QRhiTextureRenderTargetDescription &desc,
+ Flags flags)
+ : QRhiTextureRenderTarget(rhi, desc, flags),
+ d(new QMetalRenderTargetData)
+{
+}
+
+QMetalTextureRenderTarget::~QMetalTextureRenderTarget()
+{
+ release();
+ delete d;
+}
+
+void QMetalTextureRenderTarget::release()
+{
+ // nothing to do here
+}
+
+QRhiRenderPassDescriptor *QMetalTextureRenderTarget::newCompatibleRenderPassDescriptor()
+{
+ const QVector<QRhiColorAttachment> colorAttachments = m_desc.colorAttachments();
+ QMetalRenderPassDescriptor *rpD = new QMetalRenderPassDescriptor(m_rhi);
+ rpD->colorAttachmentCount = colorAttachments.count();
+ rpD->hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
+
+ for (int i = 0, ie = colorAttachments.count(); i != ie; ++i) {
+ QMetalTexture *texD = QRHI_RES(QMetalTexture, colorAttachments[i].texture());
+ QMetalRenderBuffer *rbD = QRHI_RES(QMetalRenderBuffer, colorAttachments[i].renderBuffer());
+ rpD->colorFormat[i] = texD ? texD->d->format : rbD->d->format;
+ }
+
+ if (m_desc.depthTexture())
+ rpD->dsFormat = QRHI_RES(QMetalTexture, m_desc.depthTexture())->d->format;
+ else if (m_desc.depthStencilBuffer())
+ rpD->dsFormat = QRHI_RES(QMetalRenderBuffer, m_desc.depthStencilBuffer())->d->format;
+
+ return rpD;
+}
+
+bool QMetalTextureRenderTarget::build()
+{
+ const QVector<QRhiColorAttachment> colorAttachments = m_desc.colorAttachments();
+ Q_ASSERT(!colorAttachments.isEmpty() || m_desc.depthTexture());
+ Q_ASSERT(!m_desc.depthStencilBuffer() || !m_desc.depthTexture());
+ const bool hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
+
+ d->colorAttCount = colorAttachments.count();
+ for (int i = 0; i < d->colorAttCount; ++i) {
+ QMetalTexture *texD = QRHI_RES(QMetalTexture, colorAttachments[i].texture());
+ QMetalRenderBuffer *rbD = QRHI_RES(QMetalRenderBuffer, colorAttachments[i].renderBuffer());
+ Q_ASSERT(texD || rbD);
+ id<MTLTexture> dst = nil;
+ if (texD) {
+ dst = texD->d->tex;
+ if (i == 0) {
+ d->pixelSize = texD->pixelSize();
+ d->sampleCount = texD->samples;
+ }
+ } else if (rbD) {
+ dst = rbD->d->tex;
+ if (i == 0) {
+ d->pixelSize = rbD->pixelSize();
+ d->sampleCount = rbD->samples;
+ }
+ }
+ QMetalRenderTargetData::ColorAtt colorAtt;
+ colorAtt.tex = dst;
+ colorAtt.layer = colorAttachments[i].layer();
+ colorAtt.level = colorAttachments[i].level();
+ QMetalTexture *resTexD = QRHI_RES(QMetalTexture, colorAttachments[i].resolveTexture());
+ colorAtt.resolveTex = resTexD ? resTexD->d->tex : nil;
+ colorAtt.resolveLayer = colorAttachments[i].resolveLayer();
+ colorAtt.resolveLevel = colorAttachments[i].resolveLevel();
+ d->fb.colorAtt[i] = colorAtt;
+ }
+ d->dpr = 1;
+
+ if (hasDepthStencil) {
+ if (m_desc.depthTexture()) {
+ QMetalTexture *depthTexD = QRHI_RES(QMetalTexture, m_desc.depthTexture());
+ d->fb.dsTex = depthTexD->d->tex;
+ d->fb.hasStencil = false;
+ d->fb.depthNeedsStore = true;
+ if (d->colorAttCount == 0) {
+ d->pixelSize = depthTexD->pixelSize();
+ d->sampleCount = depthTexD->samples;
+ }
+ } else {
+ QMetalRenderBuffer *depthRbD = QRHI_RES(QMetalRenderBuffer, m_desc.depthStencilBuffer());
+ d->fb.dsTex = depthRbD->d->tex;
+ d->fb.hasStencil = true;
+ d->fb.depthNeedsStore = false;
+ if (d->colorAttCount == 0) {
+ d->pixelSize = depthRbD->pixelSize();
+ d->sampleCount = depthRbD->samples;
+ }
+ }
+ d->dsAttCount = 1;
+ } else {
+ d->dsAttCount = 0;
+ }
+
+ return true;
+}
+
+QSize QMetalTextureRenderTarget::pixelSize() const
+{
+ return d->pixelSize;
+}
+
+float QMetalTextureRenderTarget::devicePixelRatio() const
+{
+ return d->dpr;
+}
+
+int QMetalTextureRenderTarget::sampleCount() const
+{
+ return d->sampleCount;
+}
+
+QMetalShaderResourceBindings::QMetalShaderResourceBindings(QRhiImplementation *rhi)
+ : QRhiShaderResourceBindings(rhi)
+{
+}
+
+QMetalShaderResourceBindings::~QMetalShaderResourceBindings()
+{
+ release();
+}
+
+void QMetalShaderResourceBindings::release()
+{
+ sortedBindings.clear();
+ maxBinding = -1;
+}
+
+bool QMetalShaderResourceBindings::build()
+{
+ if (!sortedBindings.isEmpty())
+ release();
+
+ sortedBindings = m_bindings;
+ std::sort(sortedBindings.begin(), sortedBindings.end(),
+ [](const QRhiShaderResourceBinding &a, const QRhiShaderResourceBinding &b)
+ {
+ return QRhiShaderResourceBindingPrivate::get(&a)->binding < QRhiShaderResourceBindingPrivate::get(&b)->binding;
+ });
+ if (!sortedBindings.isEmpty())
+ maxBinding = QRhiShaderResourceBindingPrivate::get(&sortedBindings.last())->binding;
+ else
+ maxBinding = -1;
+
+ boundResourceData.resize(sortedBindings.count());
+
+ for (int i = 0, ie = sortedBindings.count(); i != ie; ++i) {
+ const QRhiShaderResourceBindingPrivate *b = QRhiShaderResourceBindingPrivate::get(&sortedBindings[i]);
+ QMetalShaderResourceBindings::BoundResourceData &bd(boundResourceData[i]);
+ switch (b->type) {
+ case QRhiShaderResourceBinding::UniformBuffer:
+ {
+ QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, b->u.ubuf.buf);
+ bd.ubuf.id = bufD->m_id;
+ bd.ubuf.generation = bufD->generation;
+ }
+ break;
+ case QRhiShaderResourceBinding::SampledTexture:
+ {
+ QMetalTexture *texD = QRHI_RES(QMetalTexture, b->u.stex.tex);
+ QMetalSampler *samplerD = QRHI_RES(QMetalSampler, b->u.stex.sampler);
+ bd.stex.texId = texD->m_id;
+ bd.stex.texGeneration = texD->generation;
+ bd.stex.samplerId = samplerD->m_id;
+ bd.stex.samplerGeneration = samplerD->generation;
+ }
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ }
+
+ generation += 1;
+ return true;
+}
+
+QMetalGraphicsPipeline::QMetalGraphicsPipeline(QRhiImplementation *rhi)
+ : QRhiGraphicsPipeline(rhi),
+ d(new QMetalGraphicsPipelineData)
+{
+}
+
+QMetalGraphicsPipeline::~QMetalGraphicsPipeline()
+{
+ release();
+ delete d;
+}
+
+void QMetalGraphicsPipeline::release()
+{
+ QRHI_RES_RHI(QRhiMetal);
+
+ if (!d->ps)
+ return;
+
+ if (d->ps) {
+ [d->ps release];
+ d->ps = nil;
+ }
+
+ if (d->ds) {
+ [d->ds release];
+ d->ds = nil;
+ }
+
+ if (d->vsFunc) {
+ [d->vsFunc release];
+ d->vsFunc = nil;
+ }
+ if (d->vsLib) {
+ [d->vsLib release];
+ d->vsLib = nil;
+ }
+
+ if (d->fsFunc) {
+ [d->fsFunc release];
+ d->fsFunc = nil;
+ }
+ if (d->fsLib) {
+ [d->fsLib release];
+ d->fsLib = nil;
+ }
+
+ rhiD->unregisterResource(this);
+}
+
+static inline MTLVertexFormat toMetalAttributeFormat(QRhiVertexInputAttribute::Format format)
+{
+ switch (format) {
+ case QRhiVertexInputAttribute::Float4:
+ return MTLVertexFormatFloat4;
+ case QRhiVertexInputAttribute::Float3:
+ return MTLVertexFormatFloat3;
+ case QRhiVertexInputAttribute::Float2:
+ return MTLVertexFormatFloat2;
+ case QRhiVertexInputAttribute::Float:
+ return MTLVertexFormatFloat;
+ case QRhiVertexInputAttribute::UNormByte4:
+ return MTLVertexFormatUChar4Normalized;
+ case QRhiVertexInputAttribute::UNormByte2:
+ return MTLVertexFormatUChar2Normalized;
+ case QRhiVertexInputAttribute::UNormByte:
+ if (@available(macOS 10.13, iOS 11.0, *))
+ return MTLVertexFormatUCharNormalized;
+ else
+ Q_UNREACHABLE();
+ default:
+ Q_UNREACHABLE();
+ return MTLVertexFormatFloat4;
+ }
+}
+
+static inline MTLBlendFactor toMetalBlendFactor(QRhiGraphicsPipeline::BlendFactor f)
+{
+ switch (f) {
+ case QRhiGraphicsPipeline::Zero:
+ return MTLBlendFactorZero;
+ case QRhiGraphicsPipeline::One:
+ return MTLBlendFactorOne;
+ case QRhiGraphicsPipeline::SrcColor:
+ return MTLBlendFactorSourceColor;
+ case QRhiGraphicsPipeline::OneMinusSrcColor:
+ return MTLBlendFactorOneMinusSourceColor;
+ case QRhiGraphicsPipeline::DstColor:
+ return MTLBlendFactorDestinationColor;
+ case QRhiGraphicsPipeline::OneMinusDstColor:
+ return MTLBlendFactorOneMinusDestinationColor;
+ case QRhiGraphicsPipeline::SrcAlpha:
+ return MTLBlendFactorSourceAlpha;
+ case QRhiGraphicsPipeline::OneMinusSrcAlpha:
+ return MTLBlendFactorOneMinusSourceAlpha;
+ case QRhiGraphicsPipeline::DstAlpha:
+ return MTLBlendFactorDestinationAlpha;
+ case QRhiGraphicsPipeline::OneMinusDstAlpha:
+ return MTLBlendFactorOneMinusDestinationAlpha;
+ case QRhiGraphicsPipeline::ConstantColor:
+ return MTLBlendFactorBlendColor;
+ case QRhiGraphicsPipeline::ConstantAlpha:
+ return MTLBlendFactorBlendAlpha;
+ case QRhiGraphicsPipeline::OneMinusConstantColor:
+ return MTLBlendFactorOneMinusBlendColor;
+ case QRhiGraphicsPipeline::OneMinusConstantAlpha:
+ return MTLBlendFactorOneMinusBlendAlpha;
+ case QRhiGraphicsPipeline::SrcAlphaSaturate:
+ return MTLBlendFactorSourceAlphaSaturated;
+ case QRhiGraphicsPipeline::Src1Color:
+ return MTLBlendFactorSource1Color;
+ case QRhiGraphicsPipeline::OneMinusSrc1Color:
+ return MTLBlendFactorOneMinusSource1Color;
+ case QRhiGraphicsPipeline::Src1Alpha:
+ return MTLBlendFactorSource1Alpha;
+ case QRhiGraphicsPipeline::OneMinusSrc1Alpha:
+ return MTLBlendFactorOneMinusSource1Alpha;
+ default:
+ Q_UNREACHABLE();
+ return MTLBlendFactorZero;
+ }
+}
+
+static inline MTLBlendOperation toMetalBlendOp(QRhiGraphicsPipeline::BlendOp op)
+{
+ switch (op) {
+ case QRhiGraphicsPipeline::Add:
+ return MTLBlendOperationAdd;
+ case QRhiGraphicsPipeline::Subtract:
+ return MTLBlendOperationSubtract;
+ case QRhiGraphicsPipeline::ReverseSubtract:
+ return MTLBlendOperationReverseSubtract;
+ case QRhiGraphicsPipeline::Min:
+ return MTLBlendOperationMin;
+ case QRhiGraphicsPipeline::Max:
+ return MTLBlendOperationMax;
+ default:
+ Q_UNREACHABLE();
+ return MTLBlendOperationAdd;
+ }
+}
+
+static inline uint toMetalColorWriteMask(QRhiGraphicsPipeline::ColorMask c)
+{
+ uint f = 0;
+ if (c.testFlag(QRhiGraphicsPipeline::R))
+ f |= MTLColorWriteMaskRed;
+ if (c.testFlag(QRhiGraphicsPipeline::G))
+ f |= MTLColorWriteMaskGreen;
+ if (c.testFlag(QRhiGraphicsPipeline::B))
+ f |= MTLColorWriteMaskBlue;
+ if (c.testFlag(QRhiGraphicsPipeline::A))
+ f |= MTLColorWriteMaskAlpha;
+ return f;
+}
+
+static inline MTLCompareFunction toMetalCompareOp(QRhiGraphicsPipeline::CompareOp op)
+{
+ switch (op) {
+ case QRhiGraphicsPipeline::Never:
+ return MTLCompareFunctionNever;
+ case QRhiGraphicsPipeline::Less:
+ return MTLCompareFunctionLess;
+ case QRhiGraphicsPipeline::Equal:
+ return MTLCompareFunctionEqual;
+ case QRhiGraphicsPipeline::LessOrEqual:
+ return MTLCompareFunctionLessEqual;
+ case QRhiGraphicsPipeline::Greater:
+ return MTLCompareFunctionGreater;
+ case QRhiGraphicsPipeline::NotEqual:
+ return MTLCompareFunctionNotEqual;
+ case QRhiGraphicsPipeline::GreaterOrEqual:
+ return MTLCompareFunctionGreaterEqual;
+ case QRhiGraphicsPipeline::Always:
+ return MTLCompareFunctionAlways;
+ default:
+ Q_UNREACHABLE();
+ return MTLCompareFunctionAlways;
+ }
+}
+
+static inline MTLStencilOperation toMetalStencilOp(QRhiGraphicsPipeline::StencilOp op)
+{
+ switch (op) {
+ case QRhiGraphicsPipeline::StencilZero:
+ return MTLStencilOperationZero;
+ case QRhiGraphicsPipeline::Keep:
+ return MTLStencilOperationKeep;
+ case QRhiGraphicsPipeline::Replace:
+ return MTLStencilOperationReplace;
+ case QRhiGraphicsPipeline::IncrementAndClamp:
+ return MTLStencilOperationIncrementClamp;
+ case QRhiGraphicsPipeline::DecrementAndClamp:
+ return MTLStencilOperationDecrementClamp;
+ case QRhiGraphicsPipeline::Invert:
+ return MTLStencilOperationInvert;
+ case QRhiGraphicsPipeline::IncrementAndWrap:
+ return MTLStencilOperationIncrementWrap;
+ case QRhiGraphicsPipeline::DecrementAndWrap:
+ return MTLStencilOperationDecrementWrap;
+ default:
+ Q_UNREACHABLE();
+ return MTLStencilOperationKeep;
+ }
+}
+
+static inline MTLPrimitiveType toMetalPrimitiveType(QRhiGraphicsPipeline::Topology t)
+{
+ switch (t) {
+ case QRhiGraphicsPipeline::Triangles:
+ return MTLPrimitiveTypeTriangle;
+ case QRhiGraphicsPipeline::TriangleStrip:
+ return MTLPrimitiveTypeTriangleStrip;
+ case QRhiGraphicsPipeline::Lines:
+ return MTLPrimitiveTypeLine;
+ case QRhiGraphicsPipeline::LineStrip:
+ return MTLPrimitiveTypeLineStrip;
+ case QRhiGraphicsPipeline::Points:
+ return MTLPrimitiveTypePoint;
+ default:
+ Q_UNREACHABLE();
+ return MTLPrimitiveTypeTriangle;
+ }
+}
+
+static inline MTLCullMode toMetalCullMode(QRhiGraphicsPipeline::CullMode c)
+{
+ switch (c) {
+ case QRhiGraphicsPipeline::None:
+ return MTLCullModeNone;
+ case QRhiGraphicsPipeline::Front:
+ return MTLCullModeFront;
+ case QRhiGraphicsPipeline::Back:
+ return MTLCullModeBack;
+ default:
+ Q_UNREACHABLE();
+ return MTLCullModeNone;
+ }
+}
+
+id<MTLLibrary> QRhiMetalData::createMetalLib(const QShader &shader, QShader::Variant shaderVariant,
+ QString *error, QByteArray *entryPoint)
+{
+ QShaderCode mtllib = shader.shader({ QShader::MetalLibShader, 12, shaderVariant });
+ if (!mtllib.shader().isEmpty()) {
+ dispatch_data_t data = dispatch_data_create(mtllib.shader().constData(),
+ mtllib.shader().size(),
+ dispatch_get_global_queue(0, 0),
+ DISPATCH_DATA_DESTRUCTOR_DEFAULT);
+ NSError *err = nil;
+ id<MTLLibrary> lib = [dev newLibraryWithData: data error: &err];
+ dispatch_release(data);
+ if (!err) {
+ *entryPoint = mtllib.entryPoint();
+ return lib;
+ } else {
+ const QString msg = QString::fromNSString(err.localizedDescription);
+ qWarning("Failed to load metallib from baked shader: %s", qPrintable(msg));
+ }
+ }
+
+ QShaderCode mslSource = shader.shader({ QShader::MslShader, 12, shaderVariant });
+ if (mslSource.shader().isEmpty()) {
+ qWarning() << "No MSL 1.2 code found in baked shader" << shader;
+ return nil;
+ }
+
+ NSString *src = [NSString stringWithUTF8String: mslSource.shader().constData()];
+ MTLCompileOptions *opts = [[MTLCompileOptions alloc] init];
+ opts.languageVersion = MTLLanguageVersion1_2;
+ NSError *err = nil;
+ id<MTLLibrary> lib = [dev newLibraryWithSource: src options: opts error: &err];
+ [opts release];
+ // src is autoreleased
+
+ if (err) {
+ const QString msg = QString::fromNSString(err.localizedDescription);
+ *error = msg;
+ return nil;
+ }
+
+ *entryPoint = mslSource.entryPoint();
+ return lib;
+}
+
+id<MTLFunction> QRhiMetalData::createMSLShaderFunction(id<MTLLibrary> lib, const QByteArray &entryPoint)
+{
+ NSString *name = [NSString stringWithUTF8String: entryPoint.constData()];
+ id<MTLFunction> f = [lib newFunctionWithName: name];
+ [name release];
+ return f;
+}
+
+bool QMetalGraphicsPipeline::build()
+{
+ if (d->ps)
+ release();
+
+ QRHI_RES_RHI(QRhiMetal);
+
+ // same binding space for vertex and constant buffers - work it around
+ const int firstVertexBinding = QRHI_RES(QMetalShaderResourceBindings, m_shaderResourceBindings)->maxBinding + 1;
+
+ MTLVertexDescriptor *inputLayout = [MTLVertexDescriptor vertexDescriptor];
+ const QVector<QRhiVertexInputAttribute> attributes = m_vertexInputLayout.attributes();
+ for (const QRhiVertexInputAttribute &attribute : attributes) {
+ const int loc = attribute.location();
+ inputLayout.attributes[loc].format = toMetalAttributeFormat(attribute.format());
+ inputLayout.attributes[loc].offset = attribute.offset();
+ inputLayout.attributes[loc].bufferIndex = firstVertexBinding + attribute.binding();
+ }
+ const QVector<QRhiVertexInputBinding> bindings = m_vertexInputLayout.bindings();
+ for (int i = 0, ie = bindings.count(); i != ie; ++i) {
+ const QRhiVertexInputBinding &binding(bindings[i]);
+ const int layoutIdx = firstVertexBinding + i;
+ inputLayout.layouts[layoutIdx].stepFunction =
+ binding.classification() == QRhiVertexInputBinding::PerInstance
+ ? MTLVertexStepFunctionPerInstance : MTLVertexStepFunctionPerVertex;
+ inputLayout.layouts[layoutIdx].stepRate = binding.instanceStepRate();
+ inputLayout.layouts[layoutIdx].stride = binding.stride();
+ }
+
+ MTLRenderPipelineDescriptor *rpDesc = [[MTLRenderPipelineDescriptor alloc] init];
+
+ rpDesc.vertexDescriptor = inputLayout;
+
+ if (@available(macOS 10.13, iOS 11.0, *)) {
+ // Everything is immutable because we can guarantee that "neither the
+ // CPU nor the GPU will modify a buffer's contents between the time the
+ // buffer is set in a function's argument table and the time its
+ // associated command buffer completes execution" (as that's the point
+ // of our Vulkan-style buffer juggling in the first place).
+ const int vertexBufferCount = firstVertexBinding + bindings.count(); // cbuf + vbuf
+ const int fragmentBufferCount = firstVertexBinding; // cbuf
+ for (int i = 0; i < vertexBufferCount; ++i)
+ rpDesc.vertexBuffers[i].mutability = MTLMutabilityImmutable;
+ for (int i = 0; i < fragmentBufferCount; ++i)
+ rpDesc.fragmentBuffers[i].mutability = MTLMutabilityImmutable;
+ }
+
+ for (const QRhiGraphicsShaderStage &shaderStage : qAsConst(m_shaderStages)) {
+ QString error;
+ QByteArray entryPoint;
+ id<MTLLibrary> lib = rhiD->d->createMetalLib(shaderStage.shader(), shaderStage.shaderVariant(), &error, &entryPoint);
+ if (!lib) {
+ qWarning("MSL shader compilation failed: %s", qPrintable(error));
+ return false;
+ }
+ id<MTLFunction> func = rhiD->d->createMSLShaderFunction(lib, entryPoint);
+ if (!func) {
+ qWarning("MSL function for entry point %s not found", entryPoint.constData());
+ [lib release];
+ return false;
+ }
+ switch (shaderStage.type()) {
+ case QRhiGraphicsShaderStage::Vertex:
+ rpDesc.vertexFunction = func;
+ d->vsLib = lib;
+ d->vsFunc = func;
+ break;
+ case QRhiGraphicsShaderStage::Fragment:
+ rpDesc.fragmentFunction = func;
+ d->fsLib = lib;
+ d->fsFunc = func;
+ break;
+ default:
+ [func release];
+ [lib release];
+ break;
+ }
+ }
+
+ QMetalRenderPassDescriptor *rpD = QRHI_RES(QMetalRenderPassDescriptor, m_renderPassDesc);
+
+ if (rpD->colorAttachmentCount) {
+ // defaults when no targetBlends are provided
+ rpDesc.colorAttachments[0].pixelFormat = MTLPixelFormat(rpD->colorFormat[0]);
+ rpDesc.colorAttachments[0].writeMask = MTLColorWriteMaskAll;
+ rpDesc.colorAttachments[0].blendingEnabled = false;
+
+ Q_ASSERT(m_targetBlends.count() == rpD->colorAttachmentCount
+ || (m_targetBlends.isEmpty() && rpD->colorAttachmentCount == 1));
+
+ for (int i = 0, ie = m_targetBlends.count(); i != ie; ++i) {
+ const QRhiGraphicsPipeline::TargetBlend &b(m_targetBlends[i]);
+ rpDesc.colorAttachments[i].pixelFormat = MTLPixelFormat(rpD->colorFormat[i]);
+ rpDesc.colorAttachments[i].blendingEnabled = b.enable;
+ rpDesc.colorAttachments[i].sourceRGBBlendFactor = toMetalBlendFactor(b.srcColor);
+ rpDesc.colorAttachments[i].destinationRGBBlendFactor = toMetalBlendFactor(b.dstColor);
+ rpDesc.colorAttachments[i].rgbBlendOperation = toMetalBlendOp(b.opColor);
+ rpDesc.colorAttachments[i].sourceAlphaBlendFactor = toMetalBlendFactor(b.srcAlpha);
+ rpDesc.colorAttachments[i].destinationAlphaBlendFactor = toMetalBlendFactor(b.dstAlpha);
+ rpDesc.colorAttachments[i].alphaBlendOperation = toMetalBlendOp(b.opAlpha);
+ rpDesc.colorAttachments[i].writeMask = toMetalColorWriteMask(b.colorWrite);
+ }
+ }
+
+ if (rpD->hasDepthStencil) {
+ // Must only be set when a depth-stencil buffer will actually be bound,
+ // validation blows up otherwise.
+ MTLPixelFormat fmt = MTLPixelFormat(rpD->dsFormat);
+ rpDesc.depthAttachmentPixelFormat = fmt;
+ if (fmt != MTLPixelFormatDepth16Unorm && fmt != MTLPixelFormatDepth32Float)
+ rpDesc.stencilAttachmentPixelFormat = fmt;
+ }
+
+ rpDesc.sampleCount = rhiD->effectiveSampleCount(m_sampleCount);
+
+ NSError *err = nil;
+ d->ps = [rhiD->d->dev newRenderPipelineStateWithDescriptor: rpDesc error: &err];
+ if (!d->ps) {
+ const QString msg = QString::fromNSString(err.localizedDescription);
+ qWarning("Failed to create render pipeline state: %s", qPrintable(msg));
+ [rpDesc release];
+ return false;
+ }
+ [rpDesc release];
+
+ MTLDepthStencilDescriptor *dsDesc = [[MTLDepthStencilDescriptor alloc] init];
+ dsDesc.depthCompareFunction = m_depthTest ? toMetalCompareOp(m_depthOp) : MTLCompareFunctionAlways;
+ dsDesc.depthWriteEnabled = m_depthWrite;
+ if (m_stencilTest) {
+ dsDesc.frontFaceStencil = [[MTLStencilDescriptor alloc] init];
+ dsDesc.frontFaceStencil.stencilFailureOperation = toMetalStencilOp(m_stencilFront.failOp);
+ dsDesc.frontFaceStencil.depthFailureOperation = toMetalStencilOp(m_stencilFront.depthFailOp);
+ dsDesc.frontFaceStencil.depthStencilPassOperation = toMetalStencilOp(m_stencilFront.passOp);
+ dsDesc.frontFaceStencil.stencilCompareFunction = toMetalCompareOp(m_stencilFront.compareOp);
+ dsDesc.frontFaceStencil.readMask = m_stencilReadMask;
+ dsDesc.frontFaceStencil.writeMask = m_stencilWriteMask;
+
+ dsDesc.backFaceStencil = [[MTLStencilDescriptor alloc] init];
+ dsDesc.backFaceStencil.stencilFailureOperation = toMetalStencilOp(m_stencilBack.failOp);
+ dsDesc.backFaceStencil.depthFailureOperation = toMetalStencilOp(m_stencilBack.depthFailOp);
+ dsDesc.backFaceStencil.depthStencilPassOperation = toMetalStencilOp(m_stencilBack.passOp);
+ dsDesc.backFaceStencil.stencilCompareFunction = toMetalCompareOp(m_stencilBack.compareOp);
+ dsDesc.backFaceStencil.readMask = m_stencilReadMask;
+ dsDesc.backFaceStencil.writeMask = m_stencilWriteMask;
+ }
+
+ d->ds = [rhiD->d->dev newDepthStencilStateWithDescriptor: dsDesc];
+ [dsDesc release];
+
+ d->primitiveType = toMetalPrimitiveType(m_topology);
+ d->winding = m_frontFace == CCW ? MTLWindingCounterClockwise : MTLWindingClockwise;
+ d->cullMode = toMetalCullMode(m_cullMode);
+
+ lastActiveFrameSlot = -1;
+ generation += 1;
+ rhiD->registerResource(this);
+ return true;
+}
+
+QMetalCommandBuffer::QMetalCommandBuffer(QRhiImplementation *rhi)
+ : QRhiCommandBuffer(rhi),
+ d(new QMetalCommandBufferData)
+{
+ resetState();
+}
+
+QMetalCommandBuffer::~QMetalCommandBuffer()
+{
+ release();
+ delete d;
+}
+
+void QMetalCommandBuffer::release()
+{
+ // nothing to do here, we do not own the MTL cb object
+}
+
+const QRhiNativeHandles *QMetalCommandBuffer::nativeHandles()
+{
+ nativeHandlesStruct.commandBuffer = d->cb;
+ nativeHandlesStruct.encoder = d->currentPassEncoder;
+ return &nativeHandlesStruct;
+}
+
+void QMetalCommandBuffer::resetState()
+{
+ d->currentPassEncoder = nil;
+ d->currentPassRpDesc = nil;
+ resetPerPassState();
+}
+
+void QMetalCommandBuffer::resetPerPassState()
+{
+ currentTarget = nullptr;
+ resetPerPassCachedState();
+}
+
+void QMetalCommandBuffer::resetPerPassCachedState()
+{
+ currentPipeline = nullptr;
+ currentPipelineGeneration = 0;
+ currentSrb = nullptr;
+ currentSrbGeneration = 0;
+ currentResSlot = -1;
+ currentIndexBuffer = nullptr;
+
+ d->currentFirstVertexBinding = -1;
+ d->currentVertexInputsBuffers.clear();
+ d->currentVertexInputOffsets.clear();
+}
+
+QMetalSwapChain::QMetalSwapChain(QRhiImplementation *rhi)
+ : QRhiSwapChain(rhi),
+ rtWrapper(rhi),
+ cbWrapper(rhi),
+ d(new QMetalSwapChainData)
+{
+ for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
+ d->sem[i] = nullptr;
+ d->msaaTex[i] = nil;
+ }
+}
+
+QMetalSwapChain::~QMetalSwapChain()
+{
+ release();
+ delete d;
+}
+
+void QMetalSwapChain::release()
+{
+ if (!d->layer)
+ return;
+
+ for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
+ if (d->sem[i]) {
+ // the semaphores cannot be released if they do not have the initial value
+ dispatch_semaphore_wait(d->sem[i], DISPATCH_TIME_FOREVER);
+ dispatch_semaphore_signal(d->sem[i]);
+
+ dispatch_release(d->sem[i]);
+ d->sem[i] = nullptr;
+ }
+ }
+
+ for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
+ [d->msaaTex[i] release];
+ d->msaaTex[i] = nil;
+ }
+
+ d->layer = nullptr;
+
+ QRHI_RES_RHI(QRhiMetal);
+ rhiD->swapchains.remove(this);
+
+ QRHI_PROF;
+ QRHI_PROF_F(releaseSwapChain(this));
+
+ rhiD->unregisterResource(this);
+}
+
+QRhiCommandBuffer *QMetalSwapChain::currentFrameCommandBuffer()
+{
+ return &cbWrapper;
+}
+
+QRhiRenderTarget *QMetalSwapChain::currentFrameRenderTarget()
+{
+ return &rtWrapper;
+}
+
+QSize QMetalSwapChain::surfacePixelSize()
+{
+ // may be called before build, must not access other than m_*
+
+ NSView *v = (NSView *) m_window->winId();
+ if (v) {
+ CAMetalLayer *layer = (CAMetalLayer *) [v layer];
+ if (layer) {
+ CGSize size = [layer drawableSize];
+ return QSize(size.width, size.height);
+ }
+ }
+ return QSize();
+}
+
+QRhiRenderPassDescriptor *QMetalSwapChain::newCompatibleRenderPassDescriptor()
+{
+ chooseFormats(); // ensure colorFormat and similar are filled out
+
+ QRHI_RES_RHI(QRhiMetal);
+ QMetalRenderPassDescriptor *rpD = new QMetalRenderPassDescriptor(m_rhi);
+ rpD->colorAttachmentCount = 1;
+ rpD->hasDepthStencil = m_depthStencil != nullptr;
+
+ rpD->colorFormat[0] = d->colorFormat;
+
+ // m_depthStencil may not be built yet so cannot rely on computed fields in it
+ rpD->dsFormat = rhiD->d->dev.depth24Stencil8PixelFormatSupported
+ ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8;
+
+ return rpD;
+}
+
+void QMetalSwapChain::chooseFormats()
+{
+ QRHI_RES_RHI(QRhiMetal);
+ samples = rhiD->effectiveSampleCount(m_sampleCount);
+ // pick a format that is allowed for CAMetalLayer.pixelFormat
+ d->colorFormat = m_flags.testFlag(sRGB) ? MTLPixelFormatBGRA8Unorm_sRGB : MTLPixelFormatBGRA8Unorm;
+ d->rhiColorFormat = QRhiTexture::BGRA8;
+}
+
+bool QMetalSwapChain::buildOrResize()
+{
+ Q_ASSERT(m_window);
+
+ const bool needsRegistration = !window || window != m_window;
+
+ if (window && window != m_window)
+ release();
+ // else no release(), this is intentional
+
+ QRHI_RES_RHI(QRhiMetal);
+ if (needsRegistration)
+ rhiD->swapchains.insert(this);
+
+ window = m_window;
+
+ if (window->surfaceType() != QSurface::MetalSurface) {
+ qWarning("QMetalSwapChain only supports MetalSurface windows");
+ return false;
+ }
+
+ NSView *v = (NSView *) window->winId();
+ d->layer = (CAMetalLayer *) [v layer];
+ Q_ASSERT(d->layer);
+
+ chooseFormats();
+ if (d->colorFormat != d->layer.pixelFormat)
+ d->layer.pixelFormat = d->colorFormat;
+
+ if (m_flags.testFlag(UsedAsTransferSource))
+ d->layer.framebufferOnly = NO;
+
+#ifdef Q_OS_MACOS
+ if (m_flags.testFlag(NoVSync)) {
+ if (@available(macOS 10.13, *))
+ d->layer.displaySyncEnabled = NO;
+ }
+#endif
+
+ m_currentPixelSize = surfacePixelSize();
+ pixelSize = m_currentPixelSize;
+
+ [d->layer setDevice: rhiD->d->dev];
+
+ for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
+ if (!d->sem[i])
+ d->sem[i] = dispatch_semaphore_create(QMTL_FRAMES_IN_FLIGHT - 1);
+ }
+
+ currentFrameSlot = 0;
+ frameCount = 0;
+
+ ds = m_depthStencil ? QRHI_RES(QMetalRenderBuffer, m_depthStencil) : nullptr;
+ if (m_depthStencil && m_depthStencil->sampleCount() != m_sampleCount) {
+ qWarning("Depth-stencil buffer's sampleCount (%d) does not match color buffers' sample count (%d). Expect problems.",
+ m_depthStencil->sampleCount(), m_sampleCount);
+ }
+ if (m_depthStencil && m_depthStencil->pixelSize() != pixelSize) {
+ qWarning("Depth-stencil buffer's size (%dx%d) does not match the layer size (%dx%d). Expect problems.",
+ m_depthStencil->pixelSize().width(), m_depthStencil->pixelSize().height(),
+ pixelSize.width(), pixelSize.height());
+ }
+
+ rtWrapper.d->pixelSize = pixelSize;
+ rtWrapper.d->dpr = window->devicePixelRatio();
+ rtWrapper.d->sampleCount = samples;
+ rtWrapper.d->colorAttCount = 1;
+ rtWrapper.d->dsAttCount = ds ? 1 : 0;
+
+ qDebug("got CAMetalLayer, size %dx%d", pixelSize.width(), pixelSize.height());
+
+ if (samples > 1) {
+ MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init];
+ desc.textureType = MTLTextureType2DMultisample;
+ desc.pixelFormat = d->colorFormat;
+ desc.width = pixelSize.width();
+ desc.height = pixelSize.height();
+ desc.sampleCount = samples;
+ desc.resourceOptions = MTLResourceStorageModePrivate;
+ desc.storageMode = MTLStorageModePrivate;
+ desc.usage = MTLTextureUsageRenderTarget;
+ for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
+ [d->msaaTex[i] release];
+ d->msaaTex[i] = [rhiD->d->dev newTextureWithDescriptor: desc];
+ }
+ [desc release];
+ }
+
+ QRHI_PROF;
+ QRHI_PROF_F(resizeSwapChain(this, QMTL_FRAMES_IN_FLIGHT, samples > 1 ? QMTL_FRAMES_IN_FLIGHT : 0, samples));
+
+ if (needsRegistration)
+ rhiD->registerResource(this);
+
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/rhi/qrhimetal_p.h b/src/gui/rhi/qrhimetal_p.h
new file mode 100644
index 0000000000..094801c58c
--- /dev/null
+++ b/src/gui/rhi/qrhimetal_p.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gui module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QRHIMETAL_H
+#define QRHIMETAL_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qrhi_p.h>
+
+// no Metal includes here, the user code may be plain C++
+
+QT_BEGIN_NAMESPACE
+
+struct Q_GUI_EXPORT QRhiMetalInitParams : public QRhiInitParams
+{
+};
+
+struct Q_GUI_EXPORT QRhiMetalNativeHandles : public QRhiNativeHandles
+{
+ void *dev = nullptr; // id<MTLDevice>
+ void *cmdQueue = nullptr; // id<MTLCommandQueue>
+};
+
+struct Q_GUI_EXPORT QRhiMetalTextureNativeHandles : public QRhiNativeHandles
+{
+ void *texture = nullptr; // id<MTLTexture>
+};
+
+struct Q_GUI_EXPORT QRhiMetalCommandBufferNativeHandles : public QRhiNativeHandles
+{
+ void *commandBuffer = nullptr; // id<MTLCommandBuffer>
+ void *encoder = nullptr; // id<MTLRenderCommandEncoder>
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/rhi/qrhimetal_p_p.h b/src/gui/rhi/qrhimetal_p_p.h
new file mode 100644
index 0000000000..039a5bdbf6
--- /dev/null
+++ b/src/gui/rhi/qrhimetal_p_p.h
@@ -0,0 +1,410 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gui module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QRHIMETAL_P_H
+#define QRHIMETAL_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qrhimetal_p.h"
+#include "qrhi_p_p.h"
+#include <QWindow>
+
+QT_BEGIN_NAMESPACE
+
+static const int QMTL_FRAMES_IN_FLIGHT = 2;
+
+// have to hide the ObjC stuff, this header cannot contain MTL* at all
+struct QMetalBufferData;
+
+struct QMetalBuffer : public QRhiBuffer
+{
+ QMetalBuffer(QRhiImplementation *rhi, Type type, UsageFlags usage, int size);
+ ~QMetalBuffer();
+ void release() override;
+ bool build() override;
+
+ QMetalBufferData *d;
+ uint generation = 0;
+ int lastActiveFrameSlot = -1;
+ friend class QRhiMetal;
+ friend struct QMetalShaderResourceBindings;
+};
+
+struct QMetalRenderBufferData;
+
+struct QMetalRenderBuffer : public QRhiRenderBuffer
+{
+ QMetalRenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize,
+ int sampleCount, QRhiRenderBuffer::Flags flags);
+ ~QMetalRenderBuffer();
+ void release() override;
+ bool build() override;
+ QRhiTexture::Format backingFormat() const override;
+
+ QMetalRenderBufferData *d;
+ int samples = 1;
+ uint generation = 0;
+ int lastActiveFrameSlot = -1;
+ friend class QRhiMetal;
+};
+
+struct QMetalTextureData;
+
+struct QMetalTexture : public QRhiTexture
+{
+ QMetalTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize,
+ int sampleCount, Flags flags);
+ ~QMetalTexture();
+ void release() override;
+ bool build() override;
+ bool buildFrom(const QRhiNativeHandles *src) override;
+ const QRhiNativeHandles *nativeHandles() override;
+
+ bool prepareBuild(QSize *adjustedSize = nullptr);
+
+ QMetalTextureData *d;
+ QRhiMetalTextureNativeHandles nativeHandlesStruct;
+ int mipLevelCount = 0;
+ int samples = 1;
+ uint generation = 0;
+ int lastActiveFrameSlot = -1;
+ friend class QRhiMetal;
+ friend struct QMetalShaderResourceBindings;
+};
+
+struct QMetalSamplerData;
+
+struct QMetalSampler : public QRhiSampler
+{
+ QMetalSampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode,
+ AddressMode u, AddressMode v);
+ ~QMetalSampler();
+ void release() override;
+ bool build() override;
+
+ QMetalSamplerData *d;
+ uint generation = 0;
+ int lastActiveFrameSlot = -1;
+ friend class QRhiMetal;
+ friend struct QMetalShaderResourceBindings;
+};
+
+struct QMetalRenderPassDescriptor : public QRhiRenderPassDescriptor
+{
+ QMetalRenderPassDescriptor(QRhiImplementation *rhi);
+ ~QMetalRenderPassDescriptor();
+ void release() override;
+
+ // there is no MTLRenderPassDescriptor here as one will be created for each pass in beginPass()
+
+ // but the things needed for the render pipeline descriptor have to be provided
+ static const int MAX_COLOR_ATTACHMENTS = 8;
+ int colorAttachmentCount = 0;
+ bool hasDepthStencil = false;
+ int colorFormat[MAX_COLOR_ATTACHMENTS];
+ int dsFormat;
+};
+
+struct QMetalRenderTargetData;
+
+struct QMetalReferenceRenderTarget : public QRhiRenderTarget
+{
+ QMetalReferenceRenderTarget(QRhiImplementation *rhi);
+ ~QMetalReferenceRenderTarget();
+ void release() override;
+
+ QSize pixelSize() const override;
+ float devicePixelRatio() const override;
+ int sampleCount() const override;
+
+ QMetalRenderTargetData *d;
+};
+
+struct QMetalTextureRenderTarget : public QRhiTextureRenderTarget
+{
+ QMetalTextureRenderTarget(QRhiImplementation *rhi, const QRhiTextureRenderTargetDescription &desc, Flags flags);
+ ~QMetalTextureRenderTarget();
+ void release() override;
+
+ QSize pixelSize() const override;
+ float devicePixelRatio() const override;
+ int sampleCount() const override;
+
+ QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override;
+ bool build() override;
+
+ QMetalRenderTargetData *d;
+ friend class QRhiMetal;
+};
+
+struct QMetalShaderResourceBindings : public QRhiShaderResourceBindings
+{
+ QMetalShaderResourceBindings(QRhiImplementation *rhi);
+ ~QMetalShaderResourceBindings();
+ void release() override;
+ bool build() override;
+
+ QVector<QRhiShaderResourceBinding> sortedBindings;
+ int maxBinding = -1;
+
+ struct BoundUniformBufferData {
+ quint64 id;
+ uint generation;
+ };
+ struct BoundSampledTextureData {
+ quint64 texId;
+ uint texGeneration;
+ quint64 samplerId;
+ uint samplerGeneration;
+ };
+ struct BoundResourceData {
+ union {
+ BoundUniformBufferData ubuf;
+ BoundSampledTextureData stex;
+ };
+ };
+ QVector<BoundResourceData> boundResourceData;
+
+ uint generation = 0;
+ friend class QRhiMetal;
+};
+
+struct QMetalGraphicsPipelineData;
+
+struct QMetalGraphicsPipeline : public QRhiGraphicsPipeline
+{
+ QMetalGraphicsPipeline(QRhiImplementation *rhi);
+ ~QMetalGraphicsPipeline();
+ void release() override;
+ bool build() override;
+
+ QMetalGraphicsPipelineData *d;
+ uint generation = 0;
+ int lastActiveFrameSlot = -1;
+ friend class QRhiMetal;
+};
+
+struct QMetalCommandBufferData;
+struct QMetalSwapChain;
+
+struct QMetalCommandBuffer : public QRhiCommandBuffer
+{
+ QMetalCommandBuffer(QRhiImplementation *rhi);
+ ~QMetalCommandBuffer();
+ void release() override;
+
+ QMetalCommandBufferData *d = nullptr;
+ QRhiMetalCommandBufferNativeHandles nativeHandlesStruct;
+
+ QRhiRenderTarget *currentTarget;
+ QRhiGraphicsPipeline *currentPipeline;
+ uint currentPipelineGeneration;
+ QRhiShaderResourceBindings *currentSrb;
+ uint currentSrbGeneration;
+ int currentResSlot;
+ QRhiBuffer *currentIndexBuffer;
+ quint32 currentIndexOffset;
+ QRhiCommandBuffer::IndexFormat currentIndexFormat;
+
+ const QRhiNativeHandles *nativeHandles();
+ void resetState();
+ void resetPerPassState();
+ void resetPerPassCachedState();
+};
+
+struct QMetalSwapChainData;
+
+struct QMetalSwapChain : public QRhiSwapChain
+{
+ QMetalSwapChain(QRhiImplementation *rhi);
+ ~QMetalSwapChain();
+ void release() override;
+
+ QRhiCommandBuffer *currentFrameCommandBuffer() override;
+ QRhiRenderTarget *currentFrameRenderTarget() override;
+ QSize surfacePixelSize() override;
+
+ QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override;
+
+ bool buildOrResize() override;
+
+ void chooseFormats();
+
+ QWindow *window = nullptr;
+ QSize pixelSize;
+ int currentFrameSlot = 0; // 0..QMTL_FRAMES_IN_FLIGHT-1
+ int frameCount = 0;
+ int samples = 1;
+ QMetalReferenceRenderTarget rtWrapper;
+ QMetalCommandBuffer cbWrapper;
+ QMetalRenderBuffer *ds = nullptr;
+ QMetalSwapChainData *d = nullptr;
+};
+
+struct QRhiMetalData;
+
+class QRhiMetal : public QRhiImplementation
+{
+public:
+ QRhiMetal(QRhiMetalInitParams *params, QRhiMetalNativeHandles *importDevice = nullptr);
+ ~QRhiMetal();
+
+ bool create(QRhi::Flags flags) override;
+ void destroy() override;
+
+ QRhiGraphicsPipeline *createGraphicsPipeline() override;
+ QRhiShaderResourceBindings *createShaderResourceBindings() override;
+ QRhiBuffer *createBuffer(QRhiBuffer::Type type,
+ QRhiBuffer::UsageFlags usage,
+ int size) override;
+ QRhiRenderBuffer *createRenderBuffer(QRhiRenderBuffer::Type type,
+ const QSize &pixelSize,
+ int sampleCount,
+ QRhiRenderBuffer::Flags flags) override;
+ QRhiTexture *createTexture(QRhiTexture::Format format,
+ const QSize &pixelSize,
+ int sampleCount,
+ QRhiTexture::Flags flags) override;
+ QRhiSampler *createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
+ QRhiSampler::Filter mipmapMode,
+ QRhiSampler:: AddressMode u, QRhiSampler::AddressMode v) override;
+
+ QRhiTextureRenderTarget *createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
+ QRhiTextureRenderTarget::Flags flags) override;
+
+ QRhiSwapChain *createSwapChain() override;
+ QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override;
+ QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) override;
+ QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb) override;
+ QRhi::FrameOpResult endOffscreenFrame() override;
+ QRhi::FrameOpResult finish() override;
+
+ void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;
+
+ void beginPass(QRhiCommandBuffer *cb,
+ QRhiRenderTarget *rt,
+ const QColor &colorClearValue,
+ const QRhiDepthStencilClearValue &depthStencilClearValue,
+ QRhiResourceUpdateBatch *resourceUpdates) override;
+ void endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;
+
+ void setGraphicsPipeline(QRhiCommandBuffer *cb,
+ QRhiGraphicsPipeline *ps) override;
+
+ void setShaderResources(QRhiCommandBuffer *cb,
+ QRhiShaderResourceBindings *srb,
+ int dynamicOffsetCount,
+ const QRhiCommandBuffer::DynamicOffset *dynamicOffsets) override;
+
+ void setVertexInput(QRhiCommandBuffer *cb,
+ int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings,
+ QRhiBuffer *indexBuf, quint32 indexOffset,
+ QRhiCommandBuffer::IndexFormat indexFormat) override;
+
+ void setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport) override;
+ void setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) override;
+ void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) override;
+ void setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) override;
+
+ void draw(QRhiCommandBuffer *cb, quint32 vertexCount,
+ quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) override;
+
+ void drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
+ quint32 instanceCount, quint32 firstIndex,
+ qint32 vertexOffset, quint32 firstInstance) override;
+
+ void debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name) override;
+ void debugMarkEnd(QRhiCommandBuffer *cb) override;
+ void debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg) override;
+
+ const QRhiNativeHandles *nativeHandles(QRhiCommandBuffer *cb) override;
+ void beginExternal(QRhiCommandBuffer *cb) override;
+ void endExternal(QRhiCommandBuffer *cb) override;
+
+ QVector<int> supportedSampleCounts() const override;
+ int ubufAlignment() const override;
+ bool isYUpInFramebuffer() const override;
+ bool isYUpInNDC() const override;
+ bool isClipDepthZeroToOne() const override;
+ QMatrix4x4 clipSpaceCorrMatrix() const override;
+ bool isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const override;
+ bool isFeatureSupported(QRhi::Feature feature) const override;
+ int resourceLimit(QRhi::ResourceLimit limit) const override;
+ const QRhiNativeHandles *nativeHandles() override;
+ void sendVMemStatsToProfiler() override;
+
+ void executeDeferredReleases(bool forced = false);
+ void finishActiveReadbacks(bool forced = false);
+ qsizetype subresUploadByteSize(const QRhiTextureSubresourceUploadDescription &subresDesc) const;
+ void enqueueSubresUpload(QMetalTexture *texD, void *mp, void *blitEncPtr,
+ int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc,
+ qsizetype *curOfs);
+ void enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates);
+ void executeBufferHostWritesForCurrentFrame(QMetalBuffer *bufD);
+ void enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD, QMetalCommandBuffer *cbD,
+ int dynamicOffsetCount,
+ const QRhiCommandBuffer::DynamicOffset *dynamicOffsets,
+ bool offsetOnlyChange);
+ int effectiveSampleCount(int sampleCount) const;
+
+ bool importedDevice = false;
+ bool importedCmdQueue = false;
+ bool inFrame = false;
+ bool inPass = false;
+ QMetalSwapChain *currentSwapChain = nullptr;
+ QSet<QMetalSwapChain *> swapchains;
+ QRhiMetalNativeHandles nativeHandlesStruct;
+
+ struct {
+ int maxTextureSize = 4096;
+ } caps;
+
+ QRhiMetalData *d = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/rhi/qrhinull.cpp b/src/gui/rhi/qrhinull.cpp
new file mode 100644
index 0000000000..ad7b74a5c3
--- /dev/null
+++ b/src/gui/rhi/qrhinull.cpp
@@ -0,0 +1,709 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gui module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qrhinull_p_p.h"
+#include <qmath.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QRhiNullInitParams
+ \inmodule QtRhi
+ \brief Null backend specific initialization parameters.
+
+ A Null QRhi needs no special parameters for initialization.
+
+ \badcode
+ QRhiNullInitParams params;
+ rhi = QRhi::create(QRhi::Null, &params);
+ \endcode
+
+ The Null backend does not issue any graphics calls and creates no
+ resources. All QRhi operations will succeed as normal so applications can
+ still be run, albeit potentially at an unthrottled speed, depending on
+ their frame rendering strategy. The backend reports resources to
+ QRhiProfiler as usual.
+ */
+
+/*!
+ \class QRhiNullNativeHandles
+ \inmodule QtRhi
+ \brief Empty.
+ */
+
+/*!
+ \class QRhiNullTextureNativeHandles
+ \inmodule QtRhi
+ \brief Empty.
+ */
+
+QRhiNull::QRhiNull(QRhiNullInitParams *params)
+ : offscreenCommandBuffer(this)
+{
+ Q_UNUSED(params);
+}
+
+bool QRhiNull::create(QRhi::Flags flags)
+{
+ Q_UNUSED(flags);
+ return true;
+}
+
+void QRhiNull::destroy()
+{
+}
+
+QVector<int> QRhiNull::supportedSampleCounts() const
+{
+ return { 1 };
+}
+
+QRhiSwapChain *QRhiNull::createSwapChain()
+{
+ return new QNullSwapChain(this);
+}
+
+QRhiBuffer *QRhiNull::createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, int size)
+{
+ return new QNullBuffer(this, type, usage, size);
+}
+
+int QRhiNull::ubufAlignment() const
+{
+ return 256;
+}
+
+bool QRhiNull::isYUpInFramebuffer() const
+{
+ return false;
+}
+
+bool QRhiNull::isYUpInNDC() const
+{
+ return true;
+}
+
+bool QRhiNull::isClipDepthZeroToOne() const
+{
+ return true;
+}
+
+QMatrix4x4 QRhiNull::clipSpaceCorrMatrix() const
+{
+ return QMatrix4x4(); // identity
+}
+
+bool QRhiNull::isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const
+{
+ Q_UNUSED(format);
+ Q_UNUSED(flags);
+ return true;
+}
+
+bool QRhiNull::isFeatureSupported(QRhi::Feature feature) const
+{
+ Q_UNUSED(feature);
+ return true;
+}
+
+int QRhiNull::resourceLimit(QRhi::ResourceLimit limit) const
+{
+ switch (limit) {
+ case QRhi::TextureSizeMin:
+ return 1;
+ case QRhi::TextureSizeMax:
+ return 16384;
+ case QRhi::MaxColorAttachments:
+ return 8;
+ case QRhi::FramesInFlight:
+ return 2; // dummy
+ default:
+ Q_UNREACHABLE();
+ return 0;
+ }
+}
+
+const QRhiNativeHandles *QRhiNull::nativeHandles()
+{
+ return &nativeHandlesStruct;
+}
+
+void QRhiNull::sendVMemStatsToProfiler()
+{
+ // nothing to do here
+}
+
+QRhiRenderBuffer *QRhiNull::createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize,
+ int sampleCount, QRhiRenderBuffer::Flags flags)
+{
+ return new QNullRenderBuffer(this, type, pixelSize, sampleCount, flags);
+}
+
+QRhiTexture *QRhiNull::createTexture(QRhiTexture::Format format, const QSize &pixelSize,
+ int sampleCount, QRhiTexture::Flags flags)
+{
+ return new QNullTexture(this, format, pixelSize, sampleCount, flags);
+}
+
+QRhiSampler *QRhiNull::createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
+ QRhiSampler::Filter mipmapMode,
+ QRhiSampler::AddressMode u, QRhiSampler::AddressMode v)
+{
+ return new QNullSampler(this, magFilter, minFilter, mipmapMode, u, v);
+}
+
+QRhiTextureRenderTarget *QRhiNull::createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
+ QRhiTextureRenderTarget::Flags flags)
+{
+ return new QNullTextureRenderTarget(this, desc, flags);
+}
+
+QRhiGraphicsPipeline *QRhiNull::createGraphicsPipeline()
+{
+ return new QNullGraphicsPipeline(this);
+}
+
+QRhiShaderResourceBindings *QRhiNull::createShaderResourceBindings()
+{
+ return new QNullShaderResourceBindings(this);
+}
+
+void QRhiNull::setGraphicsPipeline(QRhiCommandBuffer *cb, QRhiGraphicsPipeline *ps)
+{
+ Q_UNUSED(cb);
+ Q_UNUSED(ps);
+}
+
+void QRhiNull::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBindings *srb,
+ int dynamicOffsetCount,
+ const QRhiCommandBuffer::DynamicOffset *dynamicOffsets)
+{
+ Q_UNUSED(cb);
+ Q_UNUSED(srb);
+ Q_UNUSED(dynamicOffsetCount);
+ Q_UNUSED(dynamicOffsets);
+}
+
+void QRhiNull::setVertexInput(QRhiCommandBuffer *cb,
+ int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings,
+ QRhiBuffer *indexBuf, quint32 indexOffset, QRhiCommandBuffer::IndexFormat indexFormat)
+{
+ Q_UNUSED(cb);
+ Q_UNUSED(startBinding);
+ Q_UNUSED(bindingCount);
+ Q_UNUSED(bindings);
+ Q_UNUSED(indexBuf);
+ Q_UNUSED(indexOffset);
+ Q_UNUSED(indexFormat);
+}
+
+void QRhiNull::setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport)
+{
+ Q_UNUSED(cb);
+ Q_UNUSED(viewport);
+}
+
+void QRhiNull::setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor)
+{
+ Q_UNUSED(cb);
+ Q_UNUSED(scissor);
+}
+
+void QRhiNull::setBlendConstants(QRhiCommandBuffer *cb, const QColor &c)
+{
+ Q_UNUSED(cb);
+ Q_UNUSED(c);
+}
+
+void QRhiNull::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue)
+{
+ Q_UNUSED(cb);
+ Q_UNUSED(refValue);
+}
+
+void QRhiNull::draw(QRhiCommandBuffer *cb, quint32 vertexCount,
+ quint32 instanceCount, quint32 firstVertex, quint32 firstInstance)
+{
+ Q_UNUSED(cb);
+ Q_UNUSED(vertexCount);
+ Q_UNUSED(instanceCount);
+ Q_UNUSED(firstVertex);
+ Q_UNUSED(firstInstance);
+}
+
+void QRhiNull::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
+ quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance)
+{
+ Q_UNUSED(cb);
+ Q_UNUSED(indexCount);
+ Q_UNUSED(instanceCount);
+ Q_UNUSED(firstIndex);
+ Q_UNUSED(vertexOffset);
+ Q_UNUSED(firstInstance);
+}
+
+void QRhiNull::debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name)
+{
+ Q_UNUSED(cb);
+ Q_UNUSED(name);
+}
+
+void QRhiNull::debugMarkEnd(QRhiCommandBuffer *cb)
+{
+ Q_UNUSED(cb);
+}
+
+void QRhiNull::debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg)
+{
+ Q_UNUSED(cb);
+ Q_UNUSED(msg);
+}
+
+const QRhiNativeHandles *QRhiNull::nativeHandles(QRhiCommandBuffer *cb)
+{
+ Q_UNUSED(cb);
+ return nullptr;
+}
+
+void QRhiNull::beginExternal(QRhiCommandBuffer *cb)
+{
+ Q_UNUSED(cb);
+}
+
+void QRhiNull::endExternal(QRhiCommandBuffer *cb)
+{
+ Q_UNUSED(cb);
+}
+
+QRhi::FrameOpResult QRhiNull::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags)
+{
+ Q_UNUSED(flags);
+ currentSwapChain = swapChain;
+ QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
+ QRHI_PROF_F(beginSwapChainFrame(swapChain));
+ return QRhi::FrameOpSuccess;
+}
+
+QRhi::FrameOpResult QRhiNull::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags)
+{
+ Q_UNUSED(flags);
+ QNullSwapChain *swapChainD = QRHI_RES(QNullSwapChain, swapChain);
+ QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
+ QRHI_PROF_F(endSwapChainFrame(swapChain, swapChainD->frameCount + 1));
+ QRHI_PROF_F(swapChainFrameGpuTime(swapChain, 0.000666f));
+ swapChainD->frameCount += 1;
+ currentSwapChain = nullptr;
+ return QRhi::FrameOpSuccess;
+}
+
+QRhi::FrameOpResult QRhiNull::beginOffscreenFrame(QRhiCommandBuffer **cb)
+{
+ *cb = &offscreenCommandBuffer;
+ return QRhi::FrameOpSuccess;
+}
+
+QRhi::FrameOpResult QRhiNull::endOffscreenFrame()
+{
+ return QRhi::FrameOpSuccess;
+}
+
+QRhi::FrameOpResult QRhiNull::finish()
+{
+ return QRhi::FrameOpSuccess;
+}
+
+void QRhiNull::resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ Q_UNUSED(cb);
+ QRhiResourceUpdateBatchPrivate *ud = QRhiResourceUpdateBatchPrivate::get(resourceUpdates);
+ for (const QRhiResourceUpdateBatchPrivate::TextureOp &u : ud->textureOps) {
+ if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Read) {
+ QRhiReadbackResult *result = u.read.result;
+ QRhiTexture *tex = u.read.rb.texture();
+ if (tex) {
+ result->format = tex->format();
+ result->pixelSize = q->sizeForMipLevel(u.read.rb.level(), tex->pixelSize());
+ } else {
+ Q_ASSERT(currentSwapChain);
+ result->format = QRhiTexture::RGBA8;
+ result->pixelSize = currentSwapChain->currentPixelSize();
+ }
+ quint32 byteSize = 0;
+ textureFormatInfo(result->format, result->pixelSize, nullptr, &byteSize);
+ result->data.fill(0, byteSize);
+ if (result->completed)
+ result->completed();
+ }
+ }
+ ud->free();
+}
+
+void QRhiNull::beginPass(QRhiCommandBuffer *cb,
+ QRhiRenderTarget *rt,
+ const QColor &colorClearValue,
+ const QRhiDepthStencilClearValue &depthStencilClearValue,
+ QRhiResourceUpdateBatch *resourceUpdates)
+{
+ Q_UNUSED(rt);
+ Q_UNUSED(colorClearValue);
+ Q_UNUSED(depthStencilClearValue);
+ if (resourceUpdates)
+ resourceUpdate(cb, resourceUpdates);
+}
+
+void QRhiNull::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ if (resourceUpdates)
+ resourceUpdate(cb, resourceUpdates);
+}
+
+QNullBuffer::QNullBuffer(QRhiImplementation *rhi, Type type, UsageFlags usage, int size)
+ : QRhiBuffer(rhi, type, usage, size)
+{
+}
+
+QNullBuffer::~QNullBuffer()
+{
+ release();
+}
+
+void QNullBuffer::release()
+{
+ QRHI_PROF;
+ QRHI_PROF_F(releaseBuffer(this));
+}
+
+bool QNullBuffer::build()
+{
+ QRHI_PROF;
+ QRHI_PROF_F(newBuffer(this, m_size, 1, 0));
+ return true;
+}
+
+QNullRenderBuffer::QNullRenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize,
+ int sampleCount, QRhiRenderBuffer::Flags flags)
+ : QRhiRenderBuffer(rhi, type, pixelSize, sampleCount, flags)
+{
+}
+
+QNullRenderBuffer::~QNullRenderBuffer()
+{
+ release();
+}
+
+void QNullRenderBuffer::release()
+{
+ QRHI_PROF;
+ QRHI_PROF_F(releaseRenderBuffer(this));
+}
+
+bool QNullRenderBuffer::build()
+{
+ QRHI_PROF;
+ QRHI_PROF_F(newRenderBuffer(this, false, false, 1));
+ return true;
+}
+
+QRhiTexture::Format QNullRenderBuffer::backingFormat() const
+{
+ return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat;
+}
+
+QNullTexture::QNullTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize,
+ int sampleCount, Flags flags)
+ : QRhiTexture(rhi, format, pixelSize, sampleCount, flags)
+{
+}
+
+QNullTexture::~QNullTexture()
+{
+ release();
+}
+
+void QNullTexture::release()
+{
+ QRHI_PROF;
+ QRHI_PROF_F(releaseTexture(this));
+}
+
+bool QNullTexture::build()
+{
+ const bool isCube = m_flags.testFlag(CubeMap);
+ const bool hasMipMaps = m_flags.testFlag(MipMapped);
+ QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize;
+ const int mipLevelCount = hasMipMaps ? qCeil(log2(qMax(size.width(), size.height()))) + 1 : 1;
+ QRHI_PROF;
+ QRHI_PROF_F(newTexture(this, true, mipLevelCount, isCube ? 6 : 1, 1));
+ return true;
+}
+
+bool QNullTexture::buildFrom(const QRhiNativeHandles *src)
+{
+ Q_UNUSED(src);
+ const bool isCube = m_flags.testFlag(CubeMap);
+ const bool hasMipMaps = m_flags.testFlag(MipMapped);
+ QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize;
+ const int mipLevelCount = hasMipMaps ? qCeil(log2(qMax(size.width(), size.height()))) + 1 : 1;
+ QRHI_PROF;
+ QRHI_PROF_F(newTexture(this, false, mipLevelCount, isCube ? 6 : 1, 1));
+ return true;
+}
+
+const QRhiNativeHandles *QNullTexture::nativeHandles()
+{
+ return &nativeHandlesStruct;
+}
+
+QNullSampler::QNullSampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode,
+ AddressMode u, AddressMode v)
+ : QRhiSampler(rhi, magFilter, minFilter, mipmapMode, u, v)
+{
+}
+
+QNullSampler::~QNullSampler()
+{
+ release();
+}
+
+void QNullSampler::release()
+{
+}
+
+bool QNullSampler::build()
+{
+ return true;
+}
+
+QNullRenderPassDescriptor::QNullRenderPassDescriptor(QRhiImplementation *rhi)
+ : QRhiRenderPassDescriptor(rhi)
+{
+}
+
+QNullRenderPassDescriptor::~QNullRenderPassDescriptor()
+{
+ release();
+}
+
+void QNullRenderPassDescriptor::release()
+{
+}
+
+QNullReferenceRenderTarget::QNullReferenceRenderTarget(QRhiImplementation *rhi)
+ : QRhiRenderTarget(rhi),
+ d(rhi)
+{
+}
+
+QNullReferenceRenderTarget::~QNullReferenceRenderTarget()
+{
+ release();
+}
+
+void QNullReferenceRenderTarget::release()
+{
+}
+
+QSize QNullReferenceRenderTarget::pixelSize() const
+{
+ return d.pixelSize;
+}
+
+float QNullReferenceRenderTarget::devicePixelRatio() const
+{
+ return d.dpr;
+}
+
+int QNullReferenceRenderTarget::sampleCount() const
+{
+ return 1;
+}
+
+QNullTextureRenderTarget::QNullTextureRenderTarget(QRhiImplementation *rhi,
+ const QRhiTextureRenderTargetDescription &desc,
+ Flags flags)
+ : QRhiTextureRenderTarget(rhi, desc, flags),
+ d(rhi)
+{
+}
+
+QNullTextureRenderTarget::~QNullTextureRenderTarget()
+{
+ release();
+}
+
+void QNullTextureRenderTarget::release()
+{
+}
+
+QRhiRenderPassDescriptor *QNullTextureRenderTarget::newCompatibleRenderPassDescriptor()
+{
+ return new QNullRenderPassDescriptor(m_rhi);
+}
+
+bool QNullTextureRenderTarget::build()
+{
+ d.rp = QRHI_RES(QNullRenderPassDescriptor, m_renderPassDesc);
+ const QVector<QRhiColorAttachment> colorAttachments = m_desc.colorAttachments();
+ if (!colorAttachments.isEmpty()) {
+ QRhiTexture *tex = colorAttachments.first().texture();
+ QRhiRenderBuffer *rb = colorAttachments.first().renderBuffer();
+ d.pixelSize = tex ? tex->pixelSize() : rb->pixelSize();
+ } else if (m_desc.depthStencilBuffer()) {
+ d.pixelSize = m_desc.depthStencilBuffer()->pixelSize();
+ } else if (m_desc.depthTexture()) {
+ d.pixelSize = m_desc.depthTexture()->pixelSize();
+ }
+ return true;
+}
+
+QSize QNullTextureRenderTarget::pixelSize() const
+{
+ return d.pixelSize;
+}
+
+float QNullTextureRenderTarget::devicePixelRatio() const
+{
+ return d.dpr;
+}
+
+int QNullTextureRenderTarget::sampleCount() const
+{
+ return 1;
+}
+
+QNullShaderResourceBindings::QNullShaderResourceBindings(QRhiImplementation *rhi)
+ : QRhiShaderResourceBindings(rhi)
+{
+}
+
+QNullShaderResourceBindings::~QNullShaderResourceBindings()
+{
+ release();
+}
+
+void QNullShaderResourceBindings::release()
+{
+}
+
+bool QNullShaderResourceBindings::build()
+{
+ return true;
+}
+
+QNullGraphicsPipeline::QNullGraphicsPipeline(QRhiImplementation *rhi)
+ : QRhiGraphicsPipeline(rhi)
+{
+}
+
+QNullGraphicsPipeline::~QNullGraphicsPipeline()
+{
+ release();
+}
+
+void QNullGraphicsPipeline::release()
+{
+}
+
+bool QNullGraphicsPipeline::build()
+{
+ return true;
+}
+
+QNullCommandBuffer::QNullCommandBuffer(QRhiImplementation *rhi)
+ : QRhiCommandBuffer(rhi)
+{
+}
+
+QNullCommandBuffer::~QNullCommandBuffer()
+{
+ release();
+}
+
+void QNullCommandBuffer::release()
+{
+ // nothing to do here
+}
+
+QNullSwapChain::QNullSwapChain(QRhiImplementation *rhi)
+ : QRhiSwapChain(rhi),
+ rt(rhi),
+ cb(rhi)
+{
+}
+
+QNullSwapChain::~QNullSwapChain()
+{
+ release();
+}
+
+void QNullSwapChain::release()
+{
+ QRHI_PROF;
+ QRHI_PROF_F(releaseSwapChain(this));
+}
+
+QRhiCommandBuffer *QNullSwapChain::currentFrameCommandBuffer()
+{
+ return &cb;
+}
+
+QRhiRenderTarget *QNullSwapChain::currentFrameRenderTarget()
+{
+ return &rt;
+}
+
+QSize QNullSwapChain::surfacePixelSize()
+{
+ return QSize(1280, 720);
+}
+
+QRhiRenderPassDescriptor *QNullSwapChain::newCompatibleRenderPassDescriptor()
+{
+ return new QNullRenderPassDescriptor(m_rhi);
+}
+
+bool QNullSwapChain::buildOrResize()
+{
+ m_currentPixelSize = surfacePixelSize();
+ rt.d.rp = QRHI_RES(QNullRenderPassDescriptor, m_renderPassDesc);
+ rt.d.pixelSize = m_currentPixelSize;
+ frameCount = 0;
+ QRHI_PROF;
+ QRHI_PROF_F(resizeSwapChain(this, 1, 0, 1));
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/rhi/qrhinull_p.h b/src/gui/rhi/qrhinull_p.h
new file mode 100644
index 0000000000..7d3ce5dbf1
--- /dev/null
+++ b/src/gui/rhi/qrhinull_p.h
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gui module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QRHINULL_H
+#define QRHINULL_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qrhi_p.h>
+
+QT_BEGIN_NAMESPACE
+
+struct Q_GUI_EXPORT QRhiNullInitParams : public QRhiInitParams
+{
+};
+
+struct Q_GUI_EXPORT QRhiNullNativeHandles : public QRhiNativeHandles
+{
+};
+
+struct Q_GUI_EXPORT QRhiNullTextureNativeHandles : public QRhiNativeHandles
+{
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/rhi/qrhinull_p_p.h b/src/gui/rhi/qrhinull_p_p.h
new file mode 100644
index 0000000000..da48b72656
--- /dev/null
+++ b/src/gui/rhi/qrhinull_p_p.h
@@ -0,0 +1,279 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gui module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QRHINULL_P_H
+#define QRHINULL_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qrhinull_p.h"
+#include "qrhi_p_p.h"
+
+QT_BEGIN_NAMESPACE
+
+struct QNullBuffer : public QRhiBuffer
+{
+ QNullBuffer(QRhiImplementation *rhi, Type type, UsageFlags usage, int size);
+ ~QNullBuffer();
+ void release() override;
+ bool build() override;
+};
+
+struct QNullRenderBuffer : public QRhiRenderBuffer
+{
+ QNullRenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize,
+ int sampleCount, QRhiRenderBuffer::Flags flags);
+ ~QNullRenderBuffer();
+ void release() override;
+ bool build() override;
+ QRhiTexture::Format backingFormat() const override;
+};
+
+struct QNullTexture : public QRhiTexture
+{
+ QNullTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize,
+ int sampleCount, Flags flags);
+ ~QNullTexture();
+ void release() override;
+ bool build() override;
+ bool buildFrom(const QRhiNativeHandles *src) override;
+ const QRhiNativeHandles *nativeHandles() override;
+
+ QRhiNullTextureNativeHandles nativeHandlesStruct;
+};
+
+struct QNullSampler : public QRhiSampler
+{
+ QNullSampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode,
+ AddressMode u, AddressMode v);
+ ~QNullSampler();
+ void release() override;
+ bool build() override;
+};
+
+struct QNullRenderPassDescriptor : public QRhiRenderPassDescriptor
+{
+ QNullRenderPassDescriptor(QRhiImplementation *rhi);
+ ~QNullRenderPassDescriptor();
+ void release() override;
+};
+
+struct QNullRenderTargetData
+{
+ QNullRenderTargetData(QRhiImplementation *) { }
+
+ QNullRenderPassDescriptor *rp = nullptr;
+ QSize pixelSize;
+ float dpr = 1;
+};
+
+struct QNullReferenceRenderTarget : public QRhiRenderTarget
+{
+ QNullReferenceRenderTarget(QRhiImplementation *rhi);
+ ~QNullReferenceRenderTarget();
+ void release() override;
+
+ QSize pixelSize() const override;
+ float devicePixelRatio() const override;
+ int sampleCount() const override;
+
+ QNullRenderTargetData d;
+};
+
+struct QNullTextureRenderTarget : public QRhiTextureRenderTarget
+{
+ QNullTextureRenderTarget(QRhiImplementation *rhi, const QRhiTextureRenderTargetDescription &desc, Flags flags);
+ ~QNullTextureRenderTarget();
+ void release() override;
+
+ QSize pixelSize() const override;
+ float devicePixelRatio() const override;
+ int sampleCount() const override;
+
+ QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override;
+ bool build() override;
+
+ QNullRenderTargetData d;
+};
+
+struct QNullShaderResourceBindings : public QRhiShaderResourceBindings
+{
+ QNullShaderResourceBindings(QRhiImplementation *rhi);
+ ~QNullShaderResourceBindings();
+ void release() override;
+ bool build() override;
+};
+
+struct QNullGraphicsPipeline : public QRhiGraphicsPipeline
+{
+ QNullGraphicsPipeline(QRhiImplementation *rhi);
+ ~QNullGraphicsPipeline();
+ void release() override;
+ bool build() override;
+};
+
+struct QNullCommandBuffer : public QRhiCommandBuffer
+{
+ QNullCommandBuffer(QRhiImplementation *rhi);
+ ~QNullCommandBuffer();
+ void release() override;
+};
+
+struct QNullSwapChain : public QRhiSwapChain
+{
+ QNullSwapChain(QRhiImplementation *rhi);
+ ~QNullSwapChain();
+ void release() override;
+
+ QRhiCommandBuffer *currentFrameCommandBuffer() override;
+ QRhiRenderTarget *currentFrameRenderTarget() override;
+
+ QSize surfacePixelSize() override;
+
+ QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override;
+ bool buildOrResize() override;
+
+ QNullReferenceRenderTarget rt;
+ QNullCommandBuffer cb;
+ int frameCount = 0;
+};
+
+class QRhiNull : public QRhiImplementation
+{
+public:
+ QRhiNull(QRhiNullInitParams *params);
+
+ bool create(QRhi::Flags flags) override;
+ void destroy() override;
+
+ QRhiGraphicsPipeline *createGraphicsPipeline() override;
+ QRhiShaderResourceBindings *createShaderResourceBindings() override;
+ QRhiBuffer *createBuffer(QRhiBuffer::Type type,
+ QRhiBuffer::UsageFlags usage,
+ int size) override;
+ QRhiRenderBuffer *createRenderBuffer(QRhiRenderBuffer::Type type,
+ const QSize &pixelSize,
+ int sampleCount,
+ QRhiRenderBuffer::Flags flags) override;
+ QRhiTexture *createTexture(QRhiTexture::Format format,
+ const QSize &pixelSize,
+ int sampleCount,
+ QRhiTexture::Flags flags) override;
+ QRhiSampler *createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
+ QRhiSampler::Filter mipmapMode,
+ QRhiSampler:: AddressMode u, QRhiSampler::AddressMode v) override;
+
+ QRhiTextureRenderTarget *createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
+ QRhiTextureRenderTarget::Flags flags) override;
+
+ QRhiSwapChain *createSwapChain() override;
+ QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override;
+ QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) override;
+ QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb) override;
+ QRhi::FrameOpResult endOffscreenFrame() override;
+ QRhi::FrameOpResult finish() override;
+
+ void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;
+
+ void beginPass(QRhiCommandBuffer *cb,
+ QRhiRenderTarget *rt,
+ const QColor &colorClearValue,
+ const QRhiDepthStencilClearValue &depthStencilClearValue,
+ QRhiResourceUpdateBatch *resourceUpdates) override;
+ void endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;
+
+ void setGraphicsPipeline(QRhiCommandBuffer *cb,
+ QRhiGraphicsPipeline *ps) override;
+
+ void setShaderResources(QRhiCommandBuffer *cb,
+ QRhiShaderResourceBindings *srb,
+ int dynamicOffsetCount,
+ const QRhiCommandBuffer::DynamicOffset *dynamicOffsets) override;
+
+ void setVertexInput(QRhiCommandBuffer *cb,
+ int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings,
+ QRhiBuffer *indexBuf, quint32 indexOffset,
+ QRhiCommandBuffer::IndexFormat indexFormat) override;
+
+ void setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport) override;
+ void setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) override;
+ void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) override;
+ void setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) override;
+
+ void draw(QRhiCommandBuffer *cb, quint32 vertexCount,
+ quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) override;
+
+ void drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
+ quint32 instanceCount, quint32 firstIndex,
+ qint32 vertexOffset, quint32 firstInstance) override;
+
+ void debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name) override;
+ void debugMarkEnd(QRhiCommandBuffer *cb) override;
+ void debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg) override;
+
+ const QRhiNativeHandles *nativeHandles(QRhiCommandBuffer *cb) override;
+ void beginExternal(QRhiCommandBuffer *cb) override;
+ void endExternal(QRhiCommandBuffer *cb) override;
+
+ QVector<int> supportedSampleCounts() const override;
+ int ubufAlignment() const override;
+ bool isYUpInFramebuffer() const override;
+ bool isYUpInNDC() const override;
+ bool isClipDepthZeroToOne() const override;
+ QMatrix4x4 clipSpaceCorrMatrix() const override;
+ bool isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const override;
+ bool isFeatureSupported(QRhi::Feature feature) const override;
+ int resourceLimit(QRhi::ResourceLimit limit) const override;
+ const QRhiNativeHandles *nativeHandles() override;
+ void sendVMemStatsToProfiler() override;
+
+ QRhiNullNativeHandles nativeHandlesStruct;
+ QRhiSwapChain *currentSwapChain = nullptr;
+ QNullCommandBuffer offscreenCommandBuffer;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/rhi/qrhiprofiler.cpp b/src/gui/rhi/qrhiprofiler.cpp
new file mode 100644
index 0000000000..15e3007d49
--- /dev/null
+++ b/src/gui/rhi/qrhiprofiler.cpp
@@ -0,0 +1,603 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gui module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qrhiprofiler_p_p.h"
+#include "qrhi_p_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QRhiProfiler
+ \inmodule QtRhi
+
+ \brief Collects resource and timing information from an active QRhi.
+
+ A QRhiProfiler is present for each QRhi. Query it via QRhi::profiler(). The
+ profiler is active only when the QRhi was created with
+ QRhi::EnableProfiling. No data is collected otherwise.
+
+ \note GPU timings are only available when QRhi indicates that
+ QRhi::Timestamps is supported.
+
+ Besides collecting data from the QRhi implementations, some additional
+ values are calculated. For example, for textures and similar resources the
+ profiler gives an estimate of the complete amount of memory the resource
+ needs.
+
+ \section2 Output Format
+
+ The output is comma-separated text. Each line has a number of
+ comma-separated entries and each line ends with a comma.
+
+ For example:
+
+ \badcode
+ 1,0,140446057946208,Triangle vbuf,type,0,usage,1,logical_size,84,effective_size,84,backing_gpu_buf_count,1,backing_cpu_buf_count,0,
+ 1,0,140446057947376,Triangle ubuf,type,2,usage,4,logical_size,68,effective_size,256,backing_gpu_buf_count,2,backing_cpu_buf_count,0,
+ 1,1,140446057950416,,type,0,usage,1,logical_size,112,effective_size,112,backing_gpu_buf_count,1,backing_cpu_buf_count,0,
+ 1,1,140446057950544,,type,0,usage,2,logical_size,12,effective_size,12,backing_gpu_buf_count,1,backing_cpu_buf_count,0,
+ 1,1,140446057947440,,type,2,usage,4,logical_size,68,effective_size,256,backing_gpu_buf_count,2,backing_cpu_buf_count,0,
+ 1,1,140446057984784,Cube vbuf (textured),type,0,usage,1,logical_size,720,effective_size,720,backing_gpu_buf_count,1,backing_cpu_buf_count,0,
+ 1,1,140446057982528,Cube ubuf (textured),type,2,usage,4,logical_size,68,effective_size,256,backing_gpu_buf_count,2,backing_cpu_buf_count,0,
+ 7,8,140446058913648,Qt texture,width,256,height,256,format,1,owns_native_resource,1,mip_count,9,layer_count,1,effective_sample_count,1,approx_byte_size,349524,
+ 1,8,140446058795856,Cube vbuf (textured with offscreen),type,0,usage,1,logical_size,720,effective_size,720,backing_gpu_buf_count,1,backing_cpu_buf_count,0,
+ 1,8,140446058947920,Cube ubuf (textured with offscreen),type,2,usage,4,logical_size,68,effective_size,256,backing_gpu_buf_count,2,backing_cpu_buf_count,0,
+ 7,8,140446058794928,Texture for offscreen content,width,512,height,512,format,1,owns_native_resource,1,mip_count,1,layer_count,1,effective_sample_count,1,approx_byte_size,1048576,
+ 1,8,140446058963904,Triangle vbuf,type,0,usage,1,logical_size,84,effective_size,84,backing_gpu_buf_count,1,backing_cpu_buf_count,0,
+ 1,8,140446058964560,Triangle ubuf,type,2,usage,4,logical_size,68,effective_size,256,backing_gpu_buf_count,2,backing_cpu_buf_count,0,
+ 5,9,140446057945392,,type,0,width,1280,height,720,effective_sample_count,1,transient_backing,0,winsys_backing,0,approx_byte_size,3686400,
+ 11,9,140446057944592,,width,1280,height,720,buffer_count,2,msaa_buffer_count,0,effective_sample_count,1,approx_total_byte_size,7372800,
+ 9,9,140446058913648,Qt texture,slot,0,size,262144,
+ 10,9,140446058913648,Qt texture,slot,0,
+ 17,2019,140446057944592,,frames_since_resize,121,min_ms_frame_delta,9,max_ms_frame_delta,33,Favg_ms_frame_delta,16.1167,
+ 18,2019,140446057944592,,frames_since_resize,121,min_ms_frame_build,0,max_ms_frame_build,1,Favg_ms_frame_build,0.00833333,
+ 17,4019,140446057944592,,frames_since_resize,241,min_ms_frame_delta,15,max_ms_frame_delta,17,Favg_ms_frame_delta,16.0583,
+ 18,4019,140446057944592,,frames_since_resize,241,min_ms_frame_build,0,max_ms_frame_build,0,Favg_ms_frame_build,0,
+ 12,5070,140446057944592,,
+ 2,5079,140446057947376,Triangle ubuf,
+ 2,5079,140446057946208,Triangle vbuf,
+ 2,5079,140446057947440,,
+ 2,5079,140446057950544,,
+ 2,5079,140446057950416,,
+ 8,5079,140446058913648,Qt texture,
+ 2,5079,140446057982528,Cube ubuf (textured),
+ 2,5079,140446057984784,Cube vbuf (textured),
+ 2,5079,140446058964560,Triangle ubuf,
+ 2,5079,140446058963904,Triangle vbuf,
+ 8,5079,140446058794928,Texture for offscreen content,
+ 2,5079,140446058947920,Cube ubuf (textured with offscreen),
+ 2,5079,140446058795856,Cube vbuf (textured with offscreen),
+ 6,5079,140446057945392,,
+ \endcode
+
+ Each line starts with \c op, \c timestamp, \c res, \c name where op is a
+ value from StreamOp, timestamp is a recording timestamp in milliseconds
+ (qint64), res is a number (quint64) referring to the QRhiResource the entry
+ refers to, or 0 if not applicable. \c name is the value of
+ QRhiResource::name() and may be empty as well. The \c name will never
+ contain a comma.
+
+ This is followed by any number of \c{key, value} pairs where \c key is an
+ unspecified string and \c value is a number. If \c key starts with \c F, it
+ indicates the value is a float. Otherwise assume that the value is a
+ qint64.
+ */
+
+/*!
+ \enum QRhiProfiler::StreamOp
+ Describes an entry in the profiler's output stream.
+
+ \value NewBuffer A buffer is created
+ \value ReleaseBuffer A buffer is destroyed
+ \value NewBufferStagingArea A staging buffer for buffer upload is created
+ \value ReleaseBufferStagingArea A staging buffer for buffer upload is destroyed
+ \value NewRenderBuffer A renderbuffer is created
+ \value ReleaseRenderBuffer A renderbuffer is destroyed
+ \value NewTexture A texture is created
+ \value ReleaseTexture A texture is destroyed
+ \value NewTextureStagingArea A staging buffer for texture upload is created
+ \value ReleaseTextureStagingArea A staging buffer for texture upload is destroyed
+ \value ResizeSwapChain A swapchain is created or resized
+ \value ReleaseSwapChain A swapchain is destroyed
+ \value NewReadbackBuffer A staging buffer for readback is created
+ \value ReleaseReadbackBuffer A staging buffer for readback is destroyed
+ \value GpuMemAllocStats GPU memory allocator statistics
+ \value GpuFrameTime GPU frame times
+ \value FrameToFrameTime CPU frame-to-frame times
+ \value FrameBuildTime CPU beginFrame-endFrame times
+ */
+
+/*!
+ \class QRhiProfiler::CpuTime
+ \inmodule QtRhi
+ \brief Contains CPU-side frame timings.
+
+ Once sufficient number of frames have been rendered, the minimum, maximum,
+ and average values (in milliseconds) from various measurements are made
+ available in this struct queriable from QRhiProfiler::frameToFrameTimes()
+ and QRhiProfiler::frameBuildTimes().
+
+ \sa QRhiProfiler::setFrameTimingWriteInterval()
+ */
+
+/*!
+ \class QRhiProfiler::GpuTime
+ \inmodule QtRhi
+ \brief Contains GPU-side frame timings.
+
+ Once sufficient number of frames have been rendered, the minimum, maximum,
+ and average values (in milliseconds) calculated from GPU command buffer
+ timestamps are made available in this struct queriable from
+ QRhiProfiler::gpuFrameTimes().
+
+ \sa QRhiProfiler::setFrameTimingWriteInterval()
+ */
+
+/*!
+ \internal
+ */
+QRhiProfiler::QRhiProfiler()
+ : d(new QRhiProfilerPrivate)
+{
+ d->ts.start();
+}
+
+/*!
+ Destructor.
+ */
+QRhiProfiler::~QRhiProfiler()
+{
+ // Flush because there is a high chance we have writes that were made since
+ // the event loop last ran. (esp. relevant for network devices like QTcpSocket)
+ if (d->outputDevice)
+ d->outputDevice->waitForBytesWritten(1000);
+
+ delete d;
+}
+
+/*!
+ Sets the output \a device.
+
+ \note No output will be generated when QRhi::EnableProfiling was not set.
+ */
+void QRhiProfiler::setDevice(QIODevice *device)
+{
+ d->outputDevice = device;
+}
+
+/*!
+ Requests writing a GpuMemAllocStats entry into the output, when applicable.
+ Backends that do not support this will ignore the request. This is an
+ explicit request since getting the allocator status and statistics may be
+ an expensive operation.
+ */
+void QRhiProfiler::addVMemAllocatorStats()
+{
+ if (d->rhiDWhenEnabled)
+ d->rhiDWhenEnabled->sendVMemStatsToProfiler();
+}
+
+/*!
+ \return the currently set frame timing writeout interval.
+ */
+int QRhiProfiler::frameTimingWriteInterval() const
+{
+ return d->frameTimingWriteInterval;
+}
+
+/*!
+ Sets the number of frames that need to be rendered before the collected CPU
+ and GPU timings are processed (min, max, average are calculated) to \a
+ frameCount.
+
+ The default value is 120.
+ */
+void QRhiProfiler::setFrameTimingWriteInterval(int frameCount)
+{
+ if (frameCount > 0)
+ d->frameTimingWriteInterval = frameCount;
+}
+
+/*!
+ \return min, max, and avg in milliseconds for the time that elapsed between two
+ QRhi::endFrame() calls.
+
+ \note The values are all 0 until at least frameTimingWriteInterval() frames
+ have been rendered.
+ */
+QRhiProfiler::CpuTime QRhiProfiler::frameToFrameTimes(QRhiSwapChain *sc) const
+{
+ auto it = d->swapchains.constFind(sc);
+ if (it != d->swapchains.constEnd())
+ return it->frameToFrameTime;
+
+ return QRhiProfiler::CpuTime();
+}
+
+/*!
+ \return min, max, and avg in milliseconds for the time that elapsed between
+ a QRhi::beginFrame() and QRhi::endFrame().
+
+ \note The values are all 0 until at least frameTimingWriteInterval() frames
+ have been rendered.
+ */
+QRhiProfiler::CpuTime QRhiProfiler::frameBuildTimes(QRhiSwapChain *sc) const
+{
+ auto it = d->swapchains.constFind(sc);
+ if (it != d->swapchains.constEnd())
+ return it->beginToEndFrameTime;
+
+ return QRhiProfiler::CpuTime();
+}
+
+/*!
+ \return min, max, and avg in milliseconds for the GPU time that is spent on
+ one frame.
+
+ \note The values are all 0 until at least frameTimingWriteInterval() frames
+ have been rendered.
+
+ The GPU times should only be compared between runs on the same GPU of the
+ same system with the same backend. Comparing times for different graphics
+ cards or for different backends can give misleading results. The numbers are
+ not meant to be comparable that way.
+
+ \note Some backends have no support for this, and even for those that have,
+ it is not guaranteed that the driver will support it at run time. Support
+ can be checked via QRhi::Timestamps.
+ */
+QRhiProfiler::GpuTime QRhiProfiler::gpuFrameTimes(QRhiSwapChain *sc) const
+{
+ auto it = d->swapchains.constFind(sc);
+ if (it != d->swapchains.constEnd())
+ return it->gpuFrameTime;
+
+ return QRhiProfiler::GpuTime();
+}
+
+void QRhiProfilerPrivate::startEntry(QRhiProfiler::StreamOp op, qint64 timestamp, QRhiResource *res)
+{
+ buf.clear();
+ buf.append(QByteArray::number(op));
+ buf.append(',');
+ buf.append(QByteArray::number(timestamp));
+ buf.append(',');
+ buf.append(QByteArray::number(quint64(quintptr(res))));
+ buf.append(',');
+ if (res)
+ buf.append(res->name());
+ buf.append(',');
+}
+
+void QRhiProfilerPrivate::writeInt(const char *key, qint64 v)
+{
+ Q_ASSERT(key[0] != 'F');
+ buf.append(key);
+ buf.append(',');
+ buf.append(QByteArray::number(v));
+ buf.append(',');
+}
+
+void QRhiProfilerPrivate::writeFloat(const char *key, float f)
+{
+ Q_ASSERT(key[0] == 'F');
+ buf.append(key);
+ buf.append(',');
+ buf.append(QByteArray::number(f));
+ buf.append(',');
+}
+
+void QRhiProfilerPrivate::endEntry()
+{
+ buf.append('\n');
+ outputDevice->write(buf);
+}
+
+void QRhiProfilerPrivate::newBuffer(QRhiBuffer *buf, quint32 realSize, int backingGpuBufCount, int backingCpuBufCount)
+{
+ if (!outputDevice)
+ return;
+
+ startEntry(QRhiProfiler::NewBuffer, ts.elapsed(), buf);
+ writeInt("type", buf->type());
+ writeInt("usage", buf->usage());
+ writeInt("logical_size", buf->size());
+ writeInt("effective_size", realSize);
+ writeInt("backing_gpu_buf_count", backingGpuBufCount);
+ writeInt("backing_cpu_buf_count", backingCpuBufCount);
+ endEntry();
+}
+
+void QRhiProfilerPrivate::releaseBuffer(QRhiBuffer *buf)
+{
+ if (!outputDevice)
+ return;
+
+ startEntry(QRhiProfiler::ReleaseBuffer, ts.elapsed(), buf);
+ endEntry();
+}
+
+void QRhiProfilerPrivate::newBufferStagingArea(QRhiBuffer *buf, int slot, quint32 size)
+{
+ if (!outputDevice)
+ return;
+
+ startEntry(QRhiProfiler::NewBufferStagingArea, ts.elapsed(), buf);
+ writeInt("slot", slot);
+ writeInt("size", size);
+ endEntry();
+}
+
+void QRhiProfilerPrivate::releaseBufferStagingArea(QRhiBuffer *buf, int slot)
+{
+ if (!outputDevice)
+ return;
+
+ startEntry(QRhiProfiler::ReleaseBufferStagingArea, ts.elapsed(), buf);
+ writeInt("slot", slot);
+ endEntry();
+}
+
+void QRhiProfilerPrivate::newRenderBuffer(QRhiRenderBuffer *rb, bool transientBacking, bool winSysBacking, int sampleCount)
+{
+ if (!outputDevice)
+ return;
+
+ const QRhiRenderBuffer::Type type = rb->type();
+ const QSize sz = rb->pixelSize();
+ // just make up something, ds is likely D24S8 while color is RGBA8 or similar
+ const QRhiTexture::Format assumedFormat = type == QRhiRenderBuffer::DepthStencil ? QRhiTexture::D32F : QRhiTexture::RGBA8;
+ quint32 byteSize = rhiDWhenEnabled->approxByteSizeForTexture(assumedFormat, sz, 1, 1);
+ if (sampleCount > 1)
+ byteSize *= sampleCount;
+
+ startEntry(QRhiProfiler::NewRenderBuffer, ts.elapsed(), rb);
+ writeInt("type", type);
+ writeInt("width", sz.width());
+ writeInt("height", sz.height());
+ writeInt("effective_sample_count", sampleCount);
+ writeInt("transient_backing", transientBacking);
+ writeInt("winsys_backing", winSysBacking);
+ writeInt("approx_byte_size", byteSize);
+ endEntry();
+}
+
+void QRhiProfilerPrivate::releaseRenderBuffer(QRhiRenderBuffer *rb)
+{
+ if (!outputDevice)
+ return;
+
+ startEntry(QRhiProfiler::ReleaseRenderBuffer, ts.elapsed(), rb);
+ endEntry();
+}
+
+void QRhiProfilerPrivate::newTexture(QRhiTexture *tex, bool owns, int mipCount, int layerCount, int sampleCount)
+{
+ if (!outputDevice)
+ return;
+
+ const QRhiTexture::Format format = tex->format();
+ const QSize sz = tex->pixelSize();
+ quint32 byteSize = rhiDWhenEnabled->approxByteSizeForTexture(format, sz, mipCount, layerCount);
+ if (sampleCount > 1)
+ byteSize *= sampleCount;
+
+ startEntry(QRhiProfiler::NewTexture, ts.elapsed(), tex);
+ writeInt("width", sz.width());
+ writeInt("height", sz.height());
+ writeInt("format", format);
+ writeInt("owns_native_resource", owns);
+ writeInt("mip_count", mipCount);
+ writeInt("layer_count", layerCount);
+ writeInt("effective_sample_count", sampleCount);
+ writeInt("approx_byte_size", byteSize);
+ endEntry();
+}
+
+void QRhiProfilerPrivate::releaseTexture(QRhiTexture *tex)
+{
+ if (!outputDevice)
+ return;
+
+ startEntry(QRhiProfiler::ReleaseTexture, ts.elapsed(), tex);
+ endEntry();
+}
+
+void QRhiProfilerPrivate::newTextureStagingArea(QRhiTexture *tex, int slot, quint32 size)
+{
+ if (!outputDevice)
+ return;
+
+ startEntry(QRhiProfiler::NewTextureStagingArea, ts.elapsed(), tex);
+ writeInt("slot", slot);
+ writeInt("size", size);
+ endEntry();
+}
+
+void QRhiProfilerPrivate::releaseTextureStagingArea(QRhiTexture *tex, int slot)
+{
+ if (!outputDevice)
+ return;
+
+ startEntry(QRhiProfiler::ReleaseTextureStagingArea, ts.elapsed(), tex);
+ writeInt("slot", slot);
+ endEntry();
+}
+
+void QRhiProfilerPrivate::resizeSwapChain(QRhiSwapChain *sc, int bufferCount, int msaaBufferCount, int sampleCount)
+{
+ if (!outputDevice)
+ return;
+
+ const QSize sz = sc->currentPixelSize();
+ quint32 byteSize = rhiDWhenEnabled->approxByteSizeForTexture(QRhiTexture::BGRA8, sz, 1, 1);
+ byteSize = byteSize * bufferCount + byteSize * msaaBufferCount * sampleCount;
+
+ startEntry(QRhiProfiler::ResizeSwapChain, ts.elapsed(), sc);
+ writeInt("width", sz.width());
+ writeInt("height", sz.height());
+ writeInt("buffer_count", bufferCount);
+ writeInt("msaa_buffer_count", msaaBufferCount);
+ writeInt("effective_sample_count", sampleCount);
+ writeInt("approx_total_byte_size", byteSize);
+ endEntry();
+}
+
+void QRhiProfilerPrivate::releaseSwapChain(QRhiSwapChain *sc)
+{
+ if (!outputDevice)
+ return;
+
+ startEntry(QRhiProfiler::ReleaseSwapChain, ts.elapsed(), sc);
+ endEntry();
+}
+
+template<typename T>
+void calcTiming(QVector<T> *vec, T *minDelta, T *maxDelta, float *avgDelta)
+{
+ if (vec->isEmpty())
+ return;
+
+ *minDelta = *maxDelta = 0;
+ float totalDelta = 0;
+ for (T delta : qAsConst(*vec)) {
+ totalDelta += float(delta);
+ if (*minDelta == 0 || delta < *minDelta)
+ *minDelta = delta;
+ if (*maxDelta == 0 || delta > *maxDelta)
+ *maxDelta = delta;
+ }
+ *avgDelta = totalDelta / vec->count();
+
+ vec->clear();
+}
+
+void QRhiProfilerPrivate::beginSwapChainFrame(QRhiSwapChain *sc)
+{
+ Sc &scd(swapchains[sc]);
+ scd.beginToEndTimer.start();
+}
+
+void QRhiProfilerPrivate::endSwapChainFrame(QRhiSwapChain *sc, int frameCount)
+{
+ Sc &scd(swapchains[sc]);
+ if (!scd.frameToFrameRunning) {
+ scd.frameToFrameTimer.start();
+ scd.frameToFrameRunning = true;
+ return;
+ }
+
+ scd.frameToFrameSamples.append(scd.frameToFrameTimer.restart());
+ if (scd.frameToFrameSamples.count() >= frameTimingWriteInterval) {
+ calcTiming(&scd.frameToFrameSamples,
+ &scd.frameToFrameTime.minTime, &scd.frameToFrameTime.maxTime, &scd.frameToFrameTime.avgTime);
+ if (outputDevice) {
+ startEntry(QRhiProfiler::FrameToFrameTime, ts.elapsed(), sc);
+ writeInt("frames_since_resize", frameCount);
+ writeInt("min_ms_frame_delta", scd.frameToFrameTime.minTime);
+ writeInt("max_ms_frame_delta", scd.frameToFrameTime.maxTime);
+ writeFloat("Favg_ms_frame_delta", scd.frameToFrameTime.avgTime);
+ endEntry();
+ }
+ }
+
+ scd.beginToEndSamples.append(scd.beginToEndTimer.elapsed());
+ if (scd.beginToEndSamples.count() >= frameTimingWriteInterval) {
+ calcTiming(&scd.beginToEndSamples,
+ &scd.beginToEndFrameTime.minTime, &scd.beginToEndFrameTime.maxTime, &scd.beginToEndFrameTime.avgTime);
+ if (outputDevice) {
+ startEntry(QRhiProfiler::FrameBuildTime, ts.elapsed(), sc);
+ writeInt("frames_since_resize", frameCount);
+ writeInt("min_ms_frame_build", scd.beginToEndFrameTime.minTime);
+ writeInt("max_ms_frame_build", scd.beginToEndFrameTime.maxTime);
+ writeFloat("Favg_ms_frame_build", scd.beginToEndFrameTime.avgTime);
+ endEntry();
+ }
+ }
+}
+
+void QRhiProfilerPrivate::swapChainFrameGpuTime(QRhiSwapChain *sc, float gpuTime)
+{
+ Sc &scd(swapchains[sc]);
+ scd.gpuFrameSamples.append(gpuTime);
+ if (scd.gpuFrameSamples.count() >= frameTimingWriteInterval) {
+ calcTiming(&scd.gpuFrameSamples,
+ &scd.gpuFrameTime.minTime, &scd.gpuFrameTime.maxTime, &scd.gpuFrameTime.avgTime);
+ if (outputDevice) {
+ startEntry(QRhiProfiler::GpuFrameTime, ts.elapsed(), sc);
+ writeFloat("Fmin_ms_gpu_frame_time", scd.gpuFrameTime.minTime);
+ writeFloat("Fmax_ms_gpu_frame_time", scd.gpuFrameTime.maxTime);
+ writeFloat("Favg_ms_gpu_frame_time", scd.gpuFrameTime.avgTime);
+ endEntry();
+ }
+ }
+}
+
+void QRhiProfilerPrivate::newReadbackBuffer(quint64 id, QRhiResource *src, quint32 size)
+{
+ if (!outputDevice)
+ return;
+
+ startEntry(QRhiProfiler::NewReadbackBuffer, ts.elapsed(), src);
+ writeInt("id", id);
+ writeInt("size", size);
+ endEntry();
+}
+
+void QRhiProfilerPrivate::releaseReadbackBuffer(quint64 id)
+{
+ if (!outputDevice)
+ return;
+
+ startEntry(QRhiProfiler::ReleaseReadbackBuffer, ts.elapsed(), nullptr);
+ writeInt("id", id);
+ endEntry();
+}
+
+void QRhiProfilerPrivate::vmemStat(int realAllocCount, int subAllocCount, quint32 totalSize, quint32 unusedSize)
+{
+ if (!outputDevice)
+ return;
+
+ startEntry(QRhiProfiler::GpuMemAllocStats, ts.elapsed(), nullptr);
+ writeInt("real_alloc_count", realAllocCount);
+ writeInt("sub_alloc_count", subAllocCount);
+ writeInt("total_size", totalSize);
+ writeInt("unused_size", unusedSize);
+ endEntry();
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/rhi/qrhiprofiler_p.h b/src/gui/rhi/qrhiprofiler_p.h
new file mode 100644
index 0000000000..89fd0a8798
--- /dev/null
+++ b/src/gui/rhi/qrhiprofiler_p.h
@@ -0,0 +1,120 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gui module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QRHIPROFILER_H
+#define QRHIPROFILER_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qrhi_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QRhiProfilerPrivate;
+class QIODevice;
+
+class Q_GUI_EXPORT QRhiProfiler
+{
+public:
+ enum StreamOp {
+ NewBuffer = 1,
+ ReleaseBuffer,
+ NewBufferStagingArea,
+ ReleaseBufferStagingArea,
+ NewRenderBuffer,
+ ReleaseRenderBuffer,
+ NewTexture,
+ ReleaseTexture,
+ NewTextureStagingArea,
+ ReleaseTextureStagingArea,
+ ResizeSwapChain,
+ ReleaseSwapChain,
+ NewReadbackBuffer,
+ ReleaseReadbackBuffer,
+ GpuMemAllocStats,
+ GpuFrameTime,
+ FrameToFrameTime,
+ FrameBuildTime
+ };
+
+ ~QRhiProfiler();
+
+ void setDevice(QIODevice *device);
+
+ void addVMemAllocatorStats();
+
+ int frameTimingWriteInterval() const;
+ void setFrameTimingWriteInterval(int frameCount);
+
+ struct CpuTime {
+ qint64 minTime = 0;
+ qint64 maxTime = 0;
+ float avgTime = 0;
+ };
+
+ struct GpuTime {
+ float minTime = 0;
+ float maxTime = 0;
+ float avgTime = 0;
+ };
+
+ CpuTime frameToFrameTimes(QRhiSwapChain *sc) const;
+ CpuTime frameBuildTimes(QRhiSwapChain *sc) const; // beginFrame - endFrame
+ GpuTime gpuFrameTimes(QRhiSwapChain *sc) const;
+
+private:
+ Q_DISABLE_COPY(QRhiProfiler)
+ QRhiProfiler();
+ QRhiProfilerPrivate *d;
+ friend class QRhiImplementation;
+ friend class QRhiProfilerPrivate;
+};
+
+Q_DECLARE_TYPEINFO(QRhiProfiler::CpuTime, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(QRhiProfiler::GpuTime, Q_MOVABLE_TYPE);
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/rhi/qrhiprofiler_p_p.h b/src/gui/rhi/qrhiprofiler_p_p.h
new file mode 100644
index 0000000000..49c6bd78ed
--- /dev/null
+++ b/src/gui/rhi/qrhiprofiler_p_p.h
@@ -0,0 +1,121 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gui module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QRHIPROFILER_P_H
+#define QRHIPROFILER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qrhiprofiler_p.h"
+#include <QElapsedTimer>
+#include <QHash>
+
+QT_BEGIN_NAMESPACE
+
+class QRhiProfilerPrivate
+{
+public:
+ static QRhiProfilerPrivate *get(QRhiProfiler *p) { return p->d; }
+
+ void newBuffer(QRhiBuffer *buf, quint32 realSize, int backingGpuBufCount, int backingCpuBufCount);
+ void releaseBuffer(QRhiBuffer *buf);
+ void newBufferStagingArea(QRhiBuffer *buf, int slot, quint32 size);
+ void releaseBufferStagingArea(QRhiBuffer *buf, int slot);
+
+ void newRenderBuffer(QRhiRenderBuffer *rb, bool transientBacking, bool winSysBacking, int sampleCount);
+ void releaseRenderBuffer(QRhiRenderBuffer *rb);
+
+ void newTexture(QRhiTexture *tex, bool owns, int mipCount, int layerCount, int sampleCount);
+ void releaseTexture(QRhiTexture *tex);
+ void newTextureStagingArea(QRhiTexture *tex, int slot, quint32 size);
+ void releaseTextureStagingArea(QRhiTexture *tex, int slot);
+
+ void resizeSwapChain(QRhiSwapChain *sc, int bufferCount, int msaaBufferCount, int sampleCount);
+ void releaseSwapChain(QRhiSwapChain *sc);
+
+ void beginSwapChainFrame(QRhiSwapChain *sc);
+ void endSwapChainFrame(QRhiSwapChain *sc, int frameCount);
+ void swapChainFrameGpuTime(QRhiSwapChain *sc, float gpuTimeMs);
+
+ void newReadbackBuffer(quint64 id, QRhiResource *src, quint32 size);
+ void releaseReadbackBuffer(quint64 id);
+
+ void vmemStat(int realAllocCount, int subAllocCount, quint32 totalSize, quint32 unusedSize);
+
+ void startEntry(QRhiProfiler::StreamOp op, qint64 timestamp, QRhiResource *res);
+ void writeInt(const char *key, qint64 v);
+ void writeFloat(const char *key, float f);
+ void endEntry();
+
+ QRhiImplementation *rhiDWhenEnabled = nullptr;
+ QIODevice *outputDevice = nullptr;
+ QElapsedTimer ts;
+ QByteArray buf;
+ static const int DEFAULT_FRAME_TIMING_WRITE_INTERVAL = 120; // frames
+ int frameTimingWriteInterval = DEFAULT_FRAME_TIMING_WRITE_INTERVAL;
+ struct Sc {
+ Sc() {
+ frameToFrameSamples.reserve(DEFAULT_FRAME_TIMING_WRITE_INTERVAL);
+ beginToEndSamples.reserve(DEFAULT_FRAME_TIMING_WRITE_INTERVAL);
+ gpuFrameSamples.reserve(DEFAULT_FRAME_TIMING_WRITE_INTERVAL);
+ }
+ QElapsedTimer frameToFrameTimer;
+ bool frameToFrameRunning = false;
+ QElapsedTimer beginToEndTimer;
+ QVector<qint64> frameToFrameSamples;
+ QVector<qint64> beginToEndSamples;
+ QVector<float> gpuFrameSamples;
+ QRhiProfiler::CpuTime frameToFrameTime;
+ QRhiProfiler::CpuTime beginToEndFrameTime;
+ QRhiProfiler::GpuTime gpuFrameTime;
+ };
+ QHash<QRhiSwapChain *, Sc> swapchains;
+};
+
+Q_DECLARE_TYPEINFO(QRhiProfilerPrivate::Sc, Q_MOVABLE_TYPE);
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/rhi/qrhivulkan.cpp b/src/gui/rhi/qrhivulkan.cpp
new file mode 100644
index 0000000000..1f80df6d0d
--- /dev/null
+++ b/src/gui/rhi/qrhivulkan.cpp
@@ -0,0 +1,5681 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gui module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qrhivulkan_p_p.h"
+#include "qrhivulkanext_p.h"
+
+#define VMA_IMPLEMENTATION
+#define VMA_STATIC_VULKAN_FUNCTIONS 0
+#define VMA_RECORDING_ENABLED 0
+#define VMA_DEDICATED_ALLOCATION 0
+#ifdef QT_DEBUG
+#define VMA_DEBUG_INITIALIZE_ALLOCATIONS 1
+#endif
+#include "vk_mem_alloc.h"
+
+#include <qmath.h>
+#include <QVulkanFunctions>
+#include <QVulkanWindow>
+
+QT_BEGIN_NAMESPACE
+
+/*
+ Vulkan 1.0 backend. Provides a double-buffered swapchain that throttles the
+ rendering thread to vsync. Textures and "static" buffers are device local,
+ and a separate, host visible staging buffer is used to upload data to them.
+ "Dynamic" buffers are in host visible memory and are duplicated (since there
+ can be 2 frames in flight). This is handled transparently to the application.
+*/
+
+/*!
+ \class QRhiVulkanInitParams
+ \inmodule QtRhi
+ \brief Vulkan specific initialization parameters.
+
+ A Vulkan-based QRhi needs at minimum a valid QVulkanInstance. It is up to
+ the user to ensure this is available and initialized. This is typically
+ done in main() similarly to the following:
+
+ \badcode
+ int main(int argc, char **argv)
+ {
+ ...
+
+ QVulkanInstance inst;
+ #ifndef Q_OS_ANDROID
+ inst.setLayers(QByteArrayList() << "VK_LAYER_LUNARG_standard_validation");
+ #else
+ inst.setLayers(QByteArrayList()
+ << "VK_LAYER_GOOGLE_threading"
+ << "VK_LAYER_LUNARG_parameter_validation"
+ << "VK_LAYER_LUNARG_object_tracker"
+ << "VK_LAYER_LUNARG_core_validation"
+ << "VK_LAYER_LUNARG_image"
+ << "VK_LAYER_LUNARG_swapchain"
+ << "VK_LAYER_GOOGLE_unique_objects");
+ #endif
+ inst.setExtensions(QByteArrayList()
+ << "VK_KHR_get_physical_device_properties2");
+ if (!inst.create())
+ qFatal("Vulkan not available");
+
+ ...
+ }
+ \endcode
+
+ The example here has two optional aspects: it enables the
+ \l{https://github.com/KhronosGroup/Vulkan-ValidationLayers}{Vulkan
+ validation layers}, when they are available, and also enables the
+ VK_KHR_get_physical_device_properties2 extension (part of Vulkan 1.1), when
+ available. The former is useful during the development phase (remember that
+ QVulkanInstance conveniently redirects messages and warnings to qDebug).
+ Avoid enabling it in production builds, however. The latter is important in
+ order to make QRhi::CustomInstanceStepRate available with Vulkan since
+ VK_EXT_vertex_attribute_divisor (part of Vulkan 1.1) depends on it. It can
+ be omitted when instanced drawing with a non-one step rate is not used.
+
+ Once this is done, a Vulkan-based QRhi can be created by passing the
+ instance and a QWindow with its surface type set to
+ QSurface::VulkanSurface:
+
+ \badcode
+ QRhiVulkanInitParams params;
+ params.inst = vulkanInstance;
+ params.window = window;
+ rhi = QRhi::create(QRhi::Vulkan, &params);
+ \endcode
+
+ The window is optional and can be omitted. This is not recommended however
+ because there is then no way to ensure presenting is supported while
+ choosing a graphics queue.
+
+ \note Even when a window is specified, QRhiSwapChain objects can be created
+ for other windows as well, as long as they all have their
+ QWindow::surfaceType() set to QSurface::VulkanSurface.
+
+ \section2 Working with existing Vulkan devices
+
+ When interoperating with another graphics engine, it may be necessary to
+ get a QRhi instance that uses the same Vulkan device. This can be achieved
+ by passing a pointer to a QRhiVulkanNativeHandles to QRhi::create().
+
+ The physical device and device object must then be set to a non-null value.
+ In addition, either the graphics queue family index or the graphics queue
+ object itself is required. Prefer the former, whenever possible since
+ deducing the index is not possible afterwards. Optionally, an existing
+ command pool object can be specified as well, and, also optionally,
+ vmemAllocator can be used to share the same
+ \l{https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator}{Vulkan
+ memory allocator} between two QRhi instances.
+
+ The QRhi does not take ownership of any of the external objects.
+ */
+
+/*!
+ \class QRhiVulkanNativeHandles
+ \inmodule QtRhi
+ \brief Collects device, queue, and other Vulkan objects that are used by the QRhi.
+
+ \note Ownership of the Vulkan objects is never transferred.
+ */
+
+/*!
+ \class QRhiVulkanTextureNativeHandles
+ \inmodule QtRhi
+ \brief Holds the Vulkan image object that is backing a QRhiTexture.
+
+ Importing and exporting Vulkan image objects that back a QRhiTexture when
+ running with the Vulkan backend is supported via this class. Ownership of
+ the Vulkan object is never transferred.
+
+ \note Memory allocation details are not exposed. This is intentional since
+ memory is typically suballocated from a bigger chunk of VkDeviceMemory, and
+ exposing the allocator details is not desirable for now.
+ */
+
+/*!
+ \class QRhiVulkanCommandBufferNativeHandles
+ \inmodule QtRhi
+ \brief Holds the Vulkan command buffer object that is backing a QRhiCommandBuffer.
+
+ \note The Vulkan command buffer object is only guaranteed to be valid, and
+ in recording state, while recording a frame. That is, between a
+ \l{QRhi::beginFrame()}{beginFrame()} - \l{QRhi::endFrame()}{endFrame()} or
+ \l{QRhi::beginOffscreenFrame()}{beginOffscreenFrame()} -
+ \l{QRhi::endOffsrceenFrame()}{endOffscreenFrame()} pair.
+ */
+
+static inline VkDeviceSize aligned(VkDeviceSize v, VkDeviceSize byteAlign)
+{
+ return (v + byteAlign - 1) & ~(byteAlign - 1);
+}
+
+static QVulkanInstance *globalVulkanInstance;
+
+static void VKAPI_PTR wrap_vkGetPhysicalDeviceProperties(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties* pProperties)
+{
+ globalVulkanInstance->functions()->vkGetPhysicalDeviceProperties(physicalDevice, pProperties);
+}
+
+static void VKAPI_PTR wrap_vkGetPhysicalDeviceMemoryProperties(VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties* pMemoryProperties)
+{
+ globalVulkanInstance->functions()->vkGetPhysicalDeviceMemoryProperties(physicalDevice, pMemoryProperties);
+}
+
+static VkResult VKAPI_PTR wrap_vkAllocateMemory(VkDevice device, const VkMemoryAllocateInfo* pAllocateInfo, const VkAllocationCallbacks* pAllocator, VkDeviceMemory* pMemory)
+{
+ return globalVulkanInstance->deviceFunctions(device)->vkAllocateMemory(device, pAllocateInfo, pAllocator, pMemory);
+}
+
+void VKAPI_PTR wrap_vkFreeMemory(VkDevice device, VkDeviceMemory memory, const VkAllocationCallbacks* pAllocator)
+{
+ globalVulkanInstance->deviceFunctions(device)->vkFreeMemory(device, memory, pAllocator);
+}
+
+VkResult VKAPI_PTR wrap_vkMapMemory(VkDevice device, VkDeviceMemory memory, VkDeviceSize offset, VkDeviceSize size, VkMemoryMapFlags flags, void** ppData)
+{
+ return globalVulkanInstance->deviceFunctions(device)->vkMapMemory(device, memory, offset, size, flags, ppData);
+}
+
+void VKAPI_PTR wrap_vkUnmapMemory(VkDevice device, VkDeviceMemory memory)
+{
+ globalVulkanInstance->deviceFunctions(device)->vkUnmapMemory(device, memory);
+}
+
+VkResult VKAPI_PTR wrap_vkFlushMappedMemoryRanges(VkDevice device, uint32_t memoryRangeCount, const VkMappedMemoryRange* pMemoryRanges)
+{
+ return globalVulkanInstance->deviceFunctions(device)->vkFlushMappedMemoryRanges(device, memoryRangeCount, pMemoryRanges);
+}
+
+VkResult VKAPI_PTR wrap_vkInvalidateMappedMemoryRanges(VkDevice device, uint32_t memoryRangeCount, const VkMappedMemoryRange* pMemoryRanges)
+{
+ return globalVulkanInstance->deviceFunctions(device)->vkInvalidateMappedMemoryRanges(device, memoryRangeCount, pMemoryRanges);
+}
+
+VkResult VKAPI_PTR wrap_vkBindBufferMemory(VkDevice device, VkBuffer buffer, VkDeviceMemory memory, VkDeviceSize memoryOffset)
+{
+ return globalVulkanInstance->deviceFunctions(device)->vkBindBufferMemory(device, buffer, memory, memoryOffset);
+}
+
+VkResult VKAPI_PTR wrap_vkBindImageMemory(VkDevice device, VkImage image, VkDeviceMemory memory, VkDeviceSize memoryOffset)
+{
+ return globalVulkanInstance->deviceFunctions(device)->vkBindImageMemory(device, image, memory, memoryOffset);
+}
+
+void VKAPI_PTR wrap_vkGetBufferMemoryRequirements(VkDevice device, VkBuffer buffer, VkMemoryRequirements* pMemoryRequirements)
+{
+ globalVulkanInstance->deviceFunctions(device)->vkGetBufferMemoryRequirements(device, buffer, pMemoryRequirements);
+}
+
+void VKAPI_PTR wrap_vkGetImageMemoryRequirements(VkDevice device, VkImage image, VkMemoryRequirements* pMemoryRequirements)
+{
+ globalVulkanInstance->deviceFunctions(device)->vkGetImageMemoryRequirements(device, image, pMemoryRequirements);
+}
+
+VkResult VKAPI_PTR wrap_vkCreateBuffer(VkDevice device, const VkBufferCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkBuffer* pBuffer)
+{
+ return globalVulkanInstance->deviceFunctions(device)->vkCreateBuffer(device, pCreateInfo, pAllocator, pBuffer);
+}
+
+void VKAPI_PTR wrap_vkDestroyBuffer(VkDevice device, VkBuffer buffer, const VkAllocationCallbacks* pAllocator)
+{
+ globalVulkanInstance->deviceFunctions(device)->vkDestroyBuffer(device, buffer, pAllocator);
+}
+
+VkResult VKAPI_PTR wrap_vkCreateImage(VkDevice device, const VkImageCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkImage* pImage)
+{
+ return globalVulkanInstance->deviceFunctions(device)->vkCreateImage(device, pCreateInfo, pAllocator, pImage);
+}
+
+void VKAPI_PTR wrap_vkDestroyImage(VkDevice device, VkImage image, const VkAllocationCallbacks* pAllocator)
+{
+ globalVulkanInstance->deviceFunctions(device)->vkDestroyImage(device, image, pAllocator);
+}
+
+static inline VmaAllocation toVmaAllocation(QVkAlloc a)
+{
+ return reinterpret_cast<VmaAllocation>(a);
+}
+
+static inline VmaAllocator toVmaAllocator(QVkAllocator a)
+{
+ return reinterpret_cast<VmaAllocator>(a);
+}
+
+QRhiVulkan::QRhiVulkan(QRhiVulkanInitParams *params, QRhiVulkanNativeHandles *importDevice)
+ : ofr(this)
+{
+ inst = params->inst;
+ maybeWindow = params->window; // may be null
+
+ importedDevice = importDevice != nullptr;
+ if (importedDevice) {
+ physDev = importDevice->physDev;
+ dev = importDevice->dev;
+ if (physDev && dev) {
+ gfxQueueFamilyIdx = importDevice->gfxQueueFamilyIdx;
+ gfxQueue = importDevice->gfxQueue;
+ if (importDevice->cmdPool) {
+ importedCmdPool = true;
+ cmdPool = importDevice->cmdPool;
+ }
+ if (importDevice->vmemAllocator) {
+ importedAllocator = true;
+ allocator = importDevice->vmemAllocator;
+ }
+ } else {
+ qWarning("No (physical) Vulkan device is given, cannot import");
+ importedDevice = false;
+ }
+ }
+}
+
+static bool qvk_debug_filter(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object,
+ size_t location, int32_t messageCode, const char *pLayerPrefix, const char *pMessage)
+{
+ Q_UNUSED(flags);
+ Q_UNUSED(objectType);
+ Q_UNUSED(object);
+ Q_UNUSED(location);
+ Q_UNUSED(messageCode);
+ Q_UNUSED(pLayerPrefix);
+
+ // Filter out certain misleading validation layer messages, as per
+ // VulkanMemoryAllocator documentation.
+ if (strstr(pMessage, "Mapping an image with layout")
+ && strstr(pMessage, "can result in undefined behavior if this memory is used by the device"))
+ {
+ return true;
+ }
+
+ return false;
+}
+
+bool QRhiVulkan::create(QRhi::Flags flags)
+{
+ Q_UNUSED(flags);
+ Q_ASSERT(inst);
+
+ globalVulkanInstance = inst; // assume this will not change during the lifetime of the entire application
+
+ f = inst->functions();
+
+ QVector<VkQueueFamilyProperties> queueFamilyProps;
+ auto queryQueueFamilyProps = [this, &queueFamilyProps] {
+ uint32_t queueCount = 0;
+ f->vkGetPhysicalDeviceQueueFamilyProperties(physDev, &queueCount, nullptr);
+ queueFamilyProps.resize(queueCount);
+ f->vkGetPhysicalDeviceQueueFamilyProperties(physDev, &queueCount, queueFamilyProps.data());
+ };
+
+ if (!importedDevice) {
+ uint32_t physDevCount = 0;
+ f->vkEnumeratePhysicalDevices(inst->vkInstance(), &physDevCount, nullptr);
+ if (!physDevCount) {
+ qWarning("No physical devices");
+ return false;
+ }
+ QVarLengthArray<VkPhysicalDevice, 4> physDevs(physDevCount);
+ VkResult err = f->vkEnumeratePhysicalDevices(inst->vkInstance(), &physDevCount, physDevs.data());
+ if (err != VK_SUCCESS || !physDevCount) {
+ qWarning("Failed to enumerate physical devices: %d", err);
+ return false;
+ }
+ int physDevIndex = -1;
+ int requestedPhysDevIndex = -1;
+ if (qEnvironmentVariableIsSet("QT_VK_PHYSICAL_DEVICE_INDEX"))
+ requestedPhysDevIndex = qEnvironmentVariableIntValue("QT_VK_PHYSICAL_DEVICE_INDEX");
+ for (uint32_t i = 0; i < physDevCount; ++i) {
+ f->vkGetPhysicalDeviceProperties(physDevs[i], &physDevProperties);
+ qDebug("Physical device %d: '%s' %d.%d.%d", i,
+ physDevProperties.deviceName,
+ VK_VERSION_MAJOR(physDevProperties.driverVersion),
+ VK_VERSION_MINOR(physDevProperties.driverVersion),
+ VK_VERSION_PATCH(physDevProperties.driverVersion));
+ if (physDevIndex < 0 && (requestedPhysDevIndex < 0 || requestedPhysDevIndex == int(i))) {
+ physDevIndex = i;
+ qDebug(" using this physical device");
+ }
+ }
+ if (physDevIndex < 0) {
+ qWarning("No matching physical device");
+ return false;
+ }
+ physDev = physDevs[physDevIndex];
+
+ queryQueueFamilyProps();
+
+ gfxQueue = VK_NULL_HANDLE;
+ gfxQueueFamilyIdx = -1;
+ int presQueueFamilyIdx = -1;
+ for (int i = 0; i < queueFamilyProps.count(); ++i) {
+ qDebug("queue family %d: flags=0x%x count=%d", i, queueFamilyProps[i].queueFlags, queueFamilyProps[i].queueCount);
+ if (gfxQueueFamilyIdx == -1
+ && (queueFamilyProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)
+ && (!maybeWindow || inst->supportsPresent(physDev, i, maybeWindow)))
+ {
+ gfxQueueFamilyIdx = i;
+ }
+ }
+ if (gfxQueueFamilyIdx != -1) {
+ presQueueFamilyIdx = gfxQueueFamilyIdx;
+ } else {
+ // ###
+ qWarning("No graphics queue that can present. This is not supported atm.");
+ }
+ if (gfxQueueFamilyIdx == -1) {
+ qWarning("No graphics queue family found");
+ return false;
+ }
+ if (presQueueFamilyIdx == -1) {
+ qWarning("No present queue family found");
+ return false;
+ }
+
+ VkDeviceQueueCreateInfo queueInfo[2];
+ const float prio[] = { 0 };
+ memset(queueInfo, 0, sizeof(queueInfo));
+ queueInfo[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
+ queueInfo[0].queueFamilyIndex = gfxQueueFamilyIdx;
+ queueInfo[0].queueCount = 1;
+ queueInfo[0].pQueuePriorities = prio;
+ if (gfxQueueFamilyIdx != presQueueFamilyIdx) {
+ queueInfo[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
+ queueInfo[1].queueFamilyIndex = presQueueFamilyIdx;
+ queueInfo[1].queueCount = 1;
+ queueInfo[1].pQueuePriorities = prio;
+ }
+
+ QVector<const char *> devLayers;
+ if (inst->layers().contains("VK_LAYER_LUNARG_standard_validation"))
+ devLayers.append("VK_LAYER_LUNARG_standard_validation");
+
+ uint32_t devExtCount = 0;
+ f->vkEnumerateDeviceExtensionProperties(physDev, nullptr, &devExtCount, nullptr);
+ QVector<VkExtensionProperties> devExts(devExtCount);
+ f->vkEnumerateDeviceExtensionProperties(physDev, nullptr, &devExtCount, devExts.data());
+ qDebug("%d device extensions available", devExts.count());
+
+ QVector<const char *> requestedDevExts;
+ requestedDevExts.append("VK_KHR_swapchain");
+
+ debugMarkersAvailable = false;
+ vertexAttribDivisorAvailable = false;
+ for (const VkExtensionProperties &ext : devExts) {
+ if (!strcmp(ext.extensionName, VK_EXT_DEBUG_MARKER_EXTENSION_NAME)) {
+ requestedDevExts.append(VK_EXT_DEBUG_MARKER_EXTENSION_NAME);
+ debugMarkersAvailable = true;
+ } else if (!strcmp(ext.extensionName, VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME)) {
+ if (inst->extensions().contains(QByteArrayLiteral("VK_KHR_get_physical_device_properties2"))) {
+ requestedDevExts.append(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME);
+ vertexAttribDivisorAvailable = true;
+ }
+ }
+ }
+
+ VkDeviceCreateInfo devInfo;
+ memset(&devInfo, 0, sizeof(devInfo));
+ devInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
+ devInfo.queueCreateInfoCount = gfxQueueFamilyIdx == presQueueFamilyIdx ? 1 : 2;
+ devInfo.pQueueCreateInfos = queueInfo;
+ devInfo.enabledLayerCount = devLayers.count();
+ devInfo.ppEnabledLayerNames = devLayers.constData();
+ devInfo.enabledExtensionCount = requestedDevExts.count();
+ devInfo.ppEnabledExtensionNames = requestedDevExts.constData();
+
+ err = f->vkCreateDevice(physDev, &devInfo, nullptr, &dev);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create device: %d", err);
+ return false;
+ }
+ }
+
+ df = inst->deviceFunctions(dev);
+
+ if (!importedCmdPool) {
+ VkCommandPoolCreateInfo poolInfo;
+ memset(&poolInfo, 0, sizeof(poolInfo));
+ poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
+ poolInfo.queueFamilyIndex = gfxQueueFamilyIdx;
+ VkResult err = df->vkCreateCommandPool(dev, &poolInfo, nullptr, &cmdPool);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create command pool: %d", err);
+ return false;
+ }
+ }
+
+ if (gfxQueueFamilyIdx != -1) {
+ // Will use one queue always, including when multiple QRhis use the
+ // same device. This has significant consequences, and cannot easily be
+ // changed (e.g. think pipeline barriers which create a dependency
+ // between commands submitted to a queue - with multiple queues
+ // additional synchronization would be needed)
+
+ if (!gfxQueue)
+ df->vkGetDeviceQueue(dev, gfxQueueFamilyIdx, 0, &gfxQueue);
+
+ if (queueFamilyProps.isEmpty())
+ queryQueueFamilyProps();
+
+ timestampValidBits = queueFamilyProps[gfxQueueFamilyIdx].timestampValidBits;
+ }
+
+ f->vkGetPhysicalDeviceProperties(physDev, &physDevProperties);
+ ubufAlign = physDevProperties.limits.minUniformBufferOffsetAlignment;
+ // helps little with an optimal offset of 1 (on some drivers) when the spec
+ // elsewhere states that the minimum bufferOffset is 4...
+ texbufAlign = qMax<VkDeviceSize>(4, physDevProperties.limits.optimalBufferCopyOffsetAlignment);
+
+ if (!importedAllocator) {
+ VmaVulkanFunctions afuncs;
+ afuncs.vkGetPhysicalDeviceProperties = wrap_vkGetPhysicalDeviceProperties;
+ afuncs.vkGetPhysicalDeviceMemoryProperties = wrap_vkGetPhysicalDeviceMemoryProperties;
+ afuncs.vkAllocateMemory = wrap_vkAllocateMemory;
+ afuncs.vkFreeMemory = wrap_vkFreeMemory;
+ afuncs.vkMapMemory = wrap_vkMapMemory;
+ afuncs.vkUnmapMemory = wrap_vkUnmapMemory;
+ afuncs.vkFlushMappedMemoryRanges = wrap_vkFlushMappedMemoryRanges;
+ afuncs.vkInvalidateMappedMemoryRanges = wrap_vkInvalidateMappedMemoryRanges;
+ afuncs.vkBindBufferMemory = wrap_vkBindBufferMemory;
+ afuncs.vkBindImageMemory = wrap_vkBindImageMemory;
+ afuncs.vkGetBufferMemoryRequirements = wrap_vkGetBufferMemoryRequirements;
+ afuncs.vkGetImageMemoryRequirements = wrap_vkGetImageMemoryRequirements;
+ afuncs.vkCreateBuffer = wrap_vkCreateBuffer;
+ afuncs.vkDestroyBuffer = wrap_vkDestroyBuffer;
+ afuncs.vkCreateImage = wrap_vkCreateImage;
+ afuncs.vkDestroyImage = wrap_vkDestroyImage;
+
+ VmaAllocatorCreateInfo allocatorInfo;
+ memset(&allocatorInfo, 0, sizeof(allocatorInfo));
+ allocatorInfo.physicalDevice = physDev;
+ allocatorInfo.device = dev;
+ allocatorInfo.pVulkanFunctions = &afuncs;
+ VmaAllocator vmaallocator;
+ VkResult err = vmaCreateAllocator(&allocatorInfo, &vmaallocator);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create allocator: %d", err);
+ return false;
+ }
+ allocator = vmaallocator;
+ }
+
+ inst->installDebugOutputFilter(qvk_debug_filter);
+
+ VkDescriptorPool pool;
+ VkResult err = createDescriptorPool(&pool);
+ if (err == VK_SUCCESS)
+ descriptorPools.append(pool);
+ else
+ qWarning("Failed to create initial descriptor pool: %d", err);
+
+ VkQueryPoolCreateInfo timestampQueryPoolInfo;
+ memset(&timestampQueryPoolInfo, 0, sizeof(timestampQueryPoolInfo));
+ timestampQueryPoolInfo.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO;
+ timestampQueryPoolInfo.queryType = VK_QUERY_TYPE_TIMESTAMP;
+ timestampQueryPoolInfo.queryCount = QVK_MAX_ACTIVE_TIMESTAMP_PAIRS * 2;
+ err = df->vkCreateQueryPool(dev, &timestampQueryPoolInfo, nullptr, &timestampQueryPool);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create timestamp query pool: %d", err);
+ return false;
+ }
+ timestampQueryPoolMap.resize(QVK_MAX_ACTIVE_TIMESTAMP_PAIRS); // 1 bit per pair
+ timestampQueryPoolMap.fill(false);
+
+ if (debugMarkersAvailable) {
+ vkCmdDebugMarkerBegin = reinterpret_cast<PFN_vkCmdDebugMarkerBeginEXT>(f->vkGetDeviceProcAddr(dev, "vkCmdDebugMarkerBeginEXT"));
+ vkCmdDebugMarkerEnd = reinterpret_cast<PFN_vkCmdDebugMarkerEndEXT>(f->vkGetDeviceProcAddr(dev, "vkCmdDebugMarkerEndEXT"));
+ vkCmdDebugMarkerInsert = reinterpret_cast<PFN_vkCmdDebugMarkerInsertEXT>(f->vkGetDeviceProcAddr(dev, "vkCmdDebugMarkerInsertEXT"));
+ vkDebugMarkerSetObjectName = reinterpret_cast<PFN_vkDebugMarkerSetObjectNameEXT>(f->vkGetDeviceProcAddr(dev, "vkDebugMarkerSetObjectNameEXT"));
+ }
+
+ nativeHandlesStruct.physDev = physDev;
+ nativeHandlesStruct.dev = dev;
+ nativeHandlesStruct.gfxQueueFamilyIdx = gfxQueueFamilyIdx;
+ nativeHandlesStruct.gfxQueue = gfxQueue;
+ nativeHandlesStruct.cmdPool = cmdPool;
+ nativeHandlesStruct.vmemAllocator = allocator;
+
+ return true;
+}
+
+void QRhiVulkan::destroy()
+{
+ if (!df)
+ return;
+
+ df->vkDeviceWaitIdle(dev);
+
+ executeDeferredReleases(true);
+ finishActiveReadbacks(true);
+
+ if (ofr.cmdFence) {
+ df->vkDestroyFence(dev, ofr.cmdFence, nullptr);
+ ofr.cmdFence = VK_NULL_HANDLE;
+ }
+
+ if (ofr.cbWrapper.cb) {
+ df->vkFreeCommandBuffers(dev, cmdPool, 1, &ofr.cbWrapper.cb);
+ ofr.cbWrapper.cb = VK_NULL_HANDLE;
+ }
+
+ if (pipelineCache) {
+ df->vkDestroyPipelineCache(dev, pipelineCache, nullptr);
+ pipelineCache = VK_NULL_HANDLE;
+ }
+
+ for (const DescriptorPoolData &pool : descriptorPools)
+ df->vkDestroyDescriptorPool(dev, pool.pool, nullptr);
+
+ descriptorPools.clear();
+
+ if (timestampQueryPool) {
+ df->vkDestroyQueryPool(dev, timestampQueryPool, nullptr);
+ timestampQueryPool = VK_NULL_HANDLE;
+ }
+
+ if (!importedAllocator && allocator) {
+ vmaDestroyAllocator(toVmaAllocator(allocator));
+ allocator = nullptr;
+ }
+
+ if (!importedCmdPool && cmdPool) {
+ df->vkDestroyCommandPool(dev, cmdPool, nullptr);
+ cmdPool = VK_NULL_HANDLE;
+ }
+
+ if (!importedDevice && dev) {
+ df->vkDestroyDevice(dev, nullptr);
+ inst->resetDeviceFunctions(dev);
+ dev = VK_NULL_HANDLE;
+ }
+
+ f = nullptr;
+ df = nullptr;
+}
+
+VkResult QRhiVulkan::createDescriptorPool(VkDescriptorPool *pool)
+{
+ VkDescriptorPoolSize descPoolSizes[] = {
+ { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, QVK_UNIFORM_BUFFERS_PER_POOL },
+ { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, QVK_UNIFORM_BUFFERS_PER_POOL },
+ { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, QVK_COMBINED_IMAGE_SAMPLERS_PER_POOL }
+ };
+ VkDescriptorPoolCreateInfo descPoolInfo;
+ memset(&descPoolInfo, 0, sizeof(descPoolInfo));
+ descPoolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
+ // Do not enable vkFreeDescriptorSets - sets are never freed on their own
+ // (good so no trouble with fragmentation), they just deref their pool
+ // which is then reset at some point (or not).
+ descPoolInfo.flags = 0;
+ descPoolInfo.maxSets = QVK_DESC_SETS_PER_POOL;
+ descPoolInfo.poolSizeCount = sizeof(descPoolSizes) / sizeof(descPoolSizes[0]);
+ descPoolInfo.pPoolSizes = descPoolSizes;
+ return df->vkCreateDescriptorPool(dev, &descPoolInfo, nullptr, pool);
+}
+
+bool QRhiVulkan::allocateDescriptorSet(VkDescriptorSetAllocateInfo *allocInfo, VkDescriptorSet *result, int *resultPoolIndex)
+{
+ auto tryAllocate = [this, allocInfo, result](int poolIndex) {
+ allocInfo->descriptorPool = descriptorPools[poolIndex].pool;
+ VkResult r = df->vkAllocateDescriptorSets(dev, allocInfo, result);
+ if (r == VK_SUCCESS)
+ descriptorPools[poolIndex].refCount += 1;
+ return r;
+ };
+
+ int lastPoolIdx = descriptorPools.count() - 1;
+ for (int i = lastPoolIdx; i >= 0; --i) {
+ if (descriptorPools[i].refCount == 0) {
+ df->vkResetDescriptorPool(dev, descriptorPools[i].pool, 0);
+ descriptorPools[i].allocedDescSets = 0;
+ }
+ if (descriptorPools[i].allocedDescSets + allocInfo->descriptorSetCount <= QVK_DESC_SETS_PER_POOL) {
+ VkResult err = tryAllocate(i);
+ if (err == VK_SUCCESS) {
+ descriptorPools[i].allocedDescSets += allocInfo->descriptorSetCount;
+ *resultPoolIndex = i;
+ return true;
+ }
+ }
+ }
+
+ VkDescriptorPool newPool;
+ VkResult poolErr = createDescriptorPool(&newPool);
+ if (poolErr == VK_SUCCESS) {
+ descriptorPools.append(newPool);
+ lastPoolIdx = descriptorPools.count() - 1;
+ VkResult err = tryAllocate(lastPoolIdx);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to allocate descriptor set from new pool too, giving up: %d", err);
+ return false;
+ }
+ descriptorPools[lastPoolIdx].allocedDescSets += allocInfo->descriptorSetCount;
+ *resultPoolIndex = lastPoolIdx;
+ return true;
+ } else {
+ qWarning("Failed to allocate new descriptor pool: %d", poolErr);
+ return false;
+ }
+}
+
+static inline VkFormat toVkTextureFormat(QRhiTexture::Format format, QRhiTexture::Flags flags)
+{
+ const bool srgb = flags.testFlag(QRhiTexture::sRGB);
+ switch (format) {
+ case QRhiTexture::RGBA8:
+ return srgb ? VK_FORMAT_R8G8B8A8_SRGB : VK_FORMAT_R8G8B8A8_UNORM;
+ case QRhiTexture::BGRA8:
+ return srgb ? VK_FORMAT_B8G8R8A8_SRGB : VK_FORMAT_B8G8R8A8_UNORM;
+ case QRhiTexture::R8:
+ return srgb ? VK_FORMAT_R8_SRGB : VK_FORMAT_R8_UNORM;
+ case QRhiTexture::R16:
+ return VK_FORMAT_R16_UNORM;
+ case QRhiTexture::RED_OR_ALPHA8:
+ return VK_FORMAT_R8_UNORM;
+
+ case QRhiTexture::RGBA16F:
+ return VK_FORMAT_R16G16B16A16_SFLOAT;
+ case QRhiTexture::RGBA32F:
+ return VK_FORMAT_R32G32B32A32_SFLOAT;
+
+ case QRhiTexture::D16:
+ return VK_FORMAT_D16_UNORM;
+ case QRhiTexture::D32F:
+ return VK_FORMAT_D32_SFLOAT;
+
+ case QRhiTexture::BC1:
+ return srgb ? VK_FORMAT_BC1_RGB_SRGB_BLOCK : VK_FORMAT_BC1_RGB_UNORM_BLOCK;
+ case QRhiTexture::BC2:
+ return srgb ? VK_FORMAT_BC2_SRGB_BLOCK : VK_FORMAT_BC2_UNORM_BLOCK;
+ case QRhiTexture::BC3:
+ return srgb ? VK_FORMAT_BC3_SRGB_BLOCK : VK_FORMAT_BC3_UNORM_BLOCK;
+ case QRhiTexture::BC4:
+ return VK_FORMAT_BC4_UNORM_BLOCK;
+ case QRhiTexture::BC5:
+ return VK_FORMAT_BC5_UNORM_BLOCK;
+ case QRhiTexture::BC6H:
+ return VK_FORMAT_BC6H_UFLOAT_BLOCK;
+ case QRhiTexture::BC7:
+ return srgb ? VK_FORMAT_BC7_SRGB_BLOCK : VK_FORMAT_BC7_UNORM_BLOCK;
+
+ case QRhiTexture::ETC2_RGB8:
+ return srgb ? VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK : VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK;
+ case QRhiTexture::ETC2_RGB8A1:
+ return srgb ? VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK : VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK;
+ case QRhiTexture::ETC2_RGBA8:
+ return srgb ? VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK : VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK;
+
+ case QRhiTexture::ASTC_4x4:
+ return srgb ? VK_FORMAT_ASTC_4x4_SRGB_BLOCK : VK_FORMAT_ASTC_4x4_UNORM_BLOCK;
+ case QRhiTexture::ASTC_5x4:
+ return srgb ? VK_FORMAT_ASTC_5x4_SRGB_BLOCK : VK_FORMAT_ASTC_5x4_UNORM_BLOCK;
+ case QRhiTexture::ASTC_5x5:
+ return srgb ? VK_FORMAT_ASTC_5x5_SRGB_BLOCK : VK_FORMAT_ASTC_5x5_UNORM_BLOCK;
+ case QRhiTexture::ASTC_6x5:
+ return srgb ? VK_FORMAT_ASTC_6x5_SRGB_BLOCK : VK_FORMAT_ASTC_6x5_UNORM_BLOCK;
+ case QRhiTexture::ASTC_6x6:
+ return srgb ? VK_FORMAT_ASTC_6x6_SRGB_BLOCK : VK_FORMAT_ASTC_6x6_UNORM_BLOCK;
+ case QRhiTexture::ASTC_8x5:
+ return srgb ? VK_FORMAT_ASTC_8x5_SRGB_BLOCK : VK_FORMAT_ASTC_8x5_UNORM_BLOCK;
+ case QRhiTexture::ASTC_8x6:
+ return srgb ? VK_FORMAT_ASTC_8x6_SRGB_BLOCK : VK_FORMAT_ASTC_8x6_UNORM_BLOCK;
+ case QRhiTexture::ASTC_8x8:
+ return srgb ? VK_FORMAT_ASTC_8x8_SRGB_BLOCK : VK_FORMAT_ASTC_8x8_UNORM_BLOCK;
+ case QRhiTexture::ASTC_10x5:
+ return srgb ? VK_FORMAT_ASTC_10x5_SRGB_BLOCK : VK_FORMAT_ASTC_10x5_UNORM_BLOCK;
+ case QRhiTexture::ASTC_10x6:
+ return srgb ? VK_FORMAT_ASTC_10x6_SRGB_BLOCK : VK_FORMAT_ASTC_10x6_UNORM_BLOCK;
+ case QRhiTexture::ASTC_10x8:
+ return srgb ? VK_FORMAT_ASTC_10x8_SRGB_BLOCK : VK_FORMAT_ASTC_10x8_UNORM_BLOCK;
+ case QRhiTexture::ASTC_10x10:
+ return srgb ? VK_FORMAT_ASTC_10x10_SRGB_BLOCK : VK_FORMAT_ASTC_10x10_UNORM_BLOCK;
+ case QRhiTexture::ASTC_12x10:
+ return srgb ? VK_FORMAT_ASTC_12x10_SRGB_BLOCK : VK_FORMAT_ASTC_12x10_UNORM_BLOCK;
+ case QRhiTexture::ASTC_12x12:
+ return srgb ? VK_FORMAT_ASTC_12x12_SRGB_BLOCK : VK_FORMAT_ASTC_12x12_UNORM_BLOCK;
+
+ default:
+ Q_UNREACHABLE();
+ return VK_FORMAT_R8G8B8A8_UNORM;
+ }
+}
+
+static inline QRhiTexture::Format colorTextureFormatFromVkFormat(VkFormat format, QRhiTexture::Flags *flags)
+{
+ switch (format) {
+ case VK_FORMAT_R8G8B8A8_UNORM:
+ return QRhiTexture::RGBA8;
+ case VK_FORMAT_R8G8B8A8_SRGB:
+ if (flags)
+ (*flags) |= QRhiTexture::sRGB;
+ return QRhiTexture::RGBA8;
+ case VK_FORMAT_B8G8R8A8_UNORM:
+ return QRhiTexture::BGRA8;
+ case VK_FORMAT_B8G8R8A8_SRGB:
+ if (flags)
+ (*flags) |= QRhiTexture::sRGB;
+ return QRhiTexture::BGRA8;
+ case VK_FORMAT_R8_UNORM:
+ return QRhiTexture::R8;
+ case VK_FORMAT_R8_SRGB:
+ if (flags)
+ (*flags) |= QRhiTexture::sRGB;
+ return QRhiTexture::R8;
+ case VK_FORMAT_R16_UNORM:
+ return QRhiTexture::R16;
+ default: // this cannot assert, must warn and return unknown
+ qWarning("VkFormat %d is not a recognized uncompressed color format", format);
+ break;
+ }
+ return QRhiTexture::UnknownFormat;
+}
+
+static inline bool isDepthTextureFormat(QRhiTexture::Format format)
+{
+ switch (format) {
+ case QRhiTexture::Format::D16:
+ Q_FALLTHROUGH();
+ case QRhiTexture::Format::D32F:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+// Transient images ("render buffers") backed by lazily allocated memory are
+// managed manually without going through vk_mem_alloc since it does not offer
+// any support for such images. This should be ok since in practice there
+// should be very few of such images.
+
+uint32_t QRhiVulkan::chooseTransientImageMemType(VkImage img, uint32_t startIndex)
+{
+ VkPhysicalDeviceMemoryProperties physDevMemProps;
+ f->vkGetPhysicalDeviceMemoryProperties(physDev, &physDevMemProps);
+
+ VkMemoryRequirements memReq;
+ df->vkGetImageMemoryRequirements(dev, img, &memReq);
+ uint32_t memTypeIndex = uint32_t(-1);
+
+ if (memReq.memoryTypeBits) {
+ // Find a device local + lazily allocated, or at least device local memtype.
+ const VkMemoryType *memType = physDevMemProps.memoryTypes;
+ bool foundDevLocal = false;
+ for (uint32_t i = startIndex; i < physDevMemProps.memoryTypeCount; ++i) {
+ if (memReq.memoryTypeBits & (1 << i)) {
+ if (memType[i].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) {
+ if (!foundDevLocal) {
+ foundDevLocal = true;
+ memTypeIndex = i;
+ }
+ if (memType[i].propertyFlags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) {
+ memTypeIndex = i;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ return memTypeIndex;
+}
+
+bool QRhiVulkan::createTransientImage(VkFormat format,
+ const QSize &pixelSize,
+ VkImageUsageFlags usage,
+ VkImageAspectFlags aspectMask,
+ VkSampleCountFlagBits samples,
+ VkDeviceMemory *mem,
+ VkImage *images,
+ VkImageView *views,
+ int count)
+{
+ VkMemoryRequirements memReq;
+ VkResult err;
+
+ for (int i = 0; i < count; ++i) {
+ VkImageCreateInfo imgInfo;
+ memset(&imgInfo, 0, sizeof(imgInfo));
+ imgInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
+ imgInfo.imageType = VK_IMAGE_TYPE_2D;
+ imgInfo.format = format;
+ imgInfo.extent.width = pixelSize.width();
+ imgInfo.extent.height = pixelSize.height();
+ imgInfo.extent.depth = 1;
+ imgInfo.mipLevels = imgInfo.arrayLayers = 1;
+ imgInfo.samples = samples;
+ imgInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
+ imgInfo.usage = usage | VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT;
+ imgInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+
+ err = df->vkCreateImage(dev, &imgInfo, nullptr, images + i);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create image: %d", err);
+ return false;
+ }
+
+ // Assume the reqs are the same since the images are same in every way.
+ // Still, call GetImageMemReq for every image, in order to prevent the
+ // validation layer from complaining.
+ df->vkGetImageMemoryRequirements(dev, images[i], &memReq);
+ }
+
+ VkMemoryAllocateInfo memInfo;
+ memset(&memInfo, 0, sizeof(memInfo));
+ memInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
+ memInfo.allocationSize = aligned(memReq.size, memReq.alignment) * count;
+
+ uint32_t startIndex = 0;
+ do {
+ memInfo.memoryTypeIndex = chooseTransientImageMemType(images[0], startIndex);
+ if (memInfo.memoryTypeIndex == uint32_t(-1)) {
+ qWarning("No suitable memory type found");
+ return false;
+ }
+ startIndex = memInfo.memoryTypeIndex + 1;
+ err = df->vkAllocateMemory(dev, &memInfo, nullptr, mem);
+ if (err != VK_SUCCESS && err != VK_ERROR_OUT_OF_DEVICE_MEMORY) {
+ qWarning("Failed to allocate image memory: %d", err);
+ return false;
+ }
+ } while (err != VK_SUCCESS);
+
+ VkDeviceSize ofs = 0;
+ for (int i = 0; i < count; ++i) {
+ err = df->vkBindImageMemory(dev, images[i], *mem, ofs);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to bind image memory: %d", err);
+ return false;
+ }
+ ofs += aligned(memReq.size, memReq.alignment);
+
+ VkImageViewCreateInfo imgViewInfo;
+ memset(&imgViewInfo, 0, sizeof(imgViewInfo));
+ imgViewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
+ imgViewInfo.image = images[i];
+ imgViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
+ imgViewInfo.format = format;
+ imgViewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
+ imgViewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
+ imgViewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
+ imgViewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
+ imgViewInfo.subresourceRange.aspectMask = aspectMask;
+ imgViewInfo.subresourceRange.levelCount = imgViewInfo.subresourceRange.layerCount = 1;
+
+ err = df->vkCreateImageView(dev, &imgViewInfo, nullptr, views + i);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create image view: %d", err);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+VkFormat QRhiVulkan::optimalDepthStencilFormat()
+{
+ if (optimalDsFormat != VK_FORMAT_UNDEFINED)
+ return optimalDsFormat;
+
+ const VkFormat dsFormatCandidates[] = {
+ VK_FORMAT_D24_UNORM_S8_UINT,
+ VK_FORMAT_D32_SFLOAT_S8_UINT,
+ VK_FORMAT_D16_UNORM_S8_UINT
+ };
+ const int dsFormatCandidateCount = sizeof(dsFormatCandidates) / sizeof(VkFormat);
+ int dsFormatIdx = 0;
+ while (dsFormatIdx < dsFormatCandidateCount) {
+ optimalDsFormat = dsFormatCandidates[dsFormatIdx];
+ VkFormatProperties fmtProp;
+ f->vkGetPhysicalDeviceFormatProperties(physDev, optimalDsFormat, &fmtProp);
+ if (fmtProp.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)
+ break;
+ ++dsFormatIdx;
+ }
+ if (dsFormatIdx == dsFormatCandidateCount)
+ qWarning("Failed to find an optimal depth-stencil format");
+
+ return optimalDsFormat;
+}
+
+bool QRhiVulkan::createDefaultRenderPass(VkRenderPass *rp, bool hasDepthStencil, VkSampleCountFlagBits samples, VkFormat colorFormat)
+{
+ VkAttachmentDescription attDesc[3];
+ memset(attDesc, 0, sizeof(attDesc));
+
+ // attachment list layout is color (1), ds (0-1), resolve (0-1)
+
+ attDesc[0].format = colorFormat;
+ attDesc[0].samples = samples;
+ attDesc[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
+ attDesc[0].storeOp = samples > VK_SAMPLE_COUNT_1_BIT ? VK_ATTACHMENT_STORE_OP_DONT_CARE : VK_ATTACHMENT_STORE_OP_STORE;
+ attDesc[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+ attDesc[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+ attDesc[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ attDesc[0].finalLayout = samples > VK_SAMPLE_COUNT_1_BIT ? VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
+
+ // clear on load + no store + lazy alloc + transient image should play
+ // nicely with tiled GPUs (no physical backing necessary for ds buffer)
+ attDesc[1].format = optimalDepthStencilFormat();
+ attDesc[1].samples = samples;
+ attDesc[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
+ attDesc[1].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+ attDesc[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
+ attDesc[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+ attDesc[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ attDesc[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
+
+ if (samples > VK_SAMPLE_COUNT_1_BIT) {
+ attDesc[2].format = colorFormat;
+ attDesc[2].samples = VK_SAMPLE_COUNT_1_BIT;
+ attDesc[2].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
+ attDesc[2].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
+ attDesc[2].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+ attDesc[2].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+ attDesc[2].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ attDesc[2].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
+ }
+
+ VkAttachmentReference colorRef = { 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
+ VkAttachmentReference dsRef = { 1, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL };
+ VkAttachmentReference resolveRef = { 2, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
+
+ VkSubpassDescription subpassDesc;
+ memset(&subpassDesc, 0, sizeof(subpassDesc));
+ subpassDesc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
+ subpassDesc.colorAttachmentCount = 1;
+ subpassDesc.pColorAttachments = &colorRef;
+ subpassDesc.pDepthStencilAttachment = hasDepthStencil ? &dsRef : nullptr;
+
+ // Replace the first implicit dep (TOP_OF_PIPE / ALL_COMMANDS) with our own.
+ VkSubpassDependency subpassDep;
+ memset(&subpassDep, 0, sizeof(subpassDep));
+ subpassDep.srcSubpass = VK_SUBPASS_EXTERNAL;
+ subpassDep.dstSubpass = 0;
+ subpassDep.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+ subpassDep.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+ subpassDep.srcAccessMask = 0;
+ subpassDep.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+
+ VkRenderPassCreateInfo rpInfo;
+ memset(&rpInfo, 0, sizeof(rpInfo));
+ rpInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
+ rpInfo.attachmentCount = 1;
+ rpInfo.pAttachments = attDesc;
+ rpInfo.subpassCount = 1;
+ rpInfo.pSubpasses = &subpassDesc;
+ rpInfo.dependencyCount = 1;
+ rpInfo.pDependencies = &subpassDep;
+
+ if (hasDepthStencil)
+ rpInfo.attachmentCount += 1;
+
+ if (samples > VK_SAMPLE_COUNT_1_BIT) {
+ rpInfo.attachmentCount += 1;
+ subpassDesc.pResolveAttachments = &resolveRef;
+ }
+
+ VkResult err = df->vkCreateRenderPass(dev, &rpInfo, nullptr, rp);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create renderpass: %d", err);
+ return false;
+ }
+
+ return true;
+}
+
+bool QRhiVulkan::createOffscreenRenderPass(VkRenderPass *rp,
+ const QVector<QRhiColorAttachment> &colorAttachments,
+ bool preserveColor,
+ bool preserveDs,
+ QRhiRenderBuffer *depthStencilBuffer,
+ QRhiTexture *depthTexture)
+{
+ QVarLengthArray<VkAttachmentDescription, 8> attDescs;
+ QVarLengthArray<VkAttachmentReference, 8> colorRefs;
+ QVarLengthArray<VkAttachmentReference, 8> resolveRefs;
+ const int colorAttCount = colorAttachments.count();
+
+ // attachment list layout is color (0-8), ds (0-1), resolve (0-8)
+
+ for (int i = 0; i < colorAttCount; ++i) {
+ QVkTexture *texD = QRHI_RES(QVkTexture, colorAttachments[i].texture());
+ QVkRenderBuffer *rbD = QRHI_RES(QVkRenderBuffer, colorAttachments[i].renderBuffer());
+ Q_ASSERT(texD || rbD);
+ const VkFormat vkformat = texD ? texD->vkformat : rbD->vkformat;
+ const VkSampleCountFlagBits samples = texD ? texD->samples : rbD->samples;
+
+ VkAttachmentDescription attDesc;
+ memset(&attDesc, 0, sizeof(attDesc));
+ attDesc.format = vkformat;
+ attDesc.samples = samples;
+ attDesc.loadOp = preserveColor ? VK_ATTACHMENT_LOAD_OP_LOAD : VK_ATTACHMENT_LOAD_OP_CLEAR;
+ attDesc.storeOp = colorAttachments[i].resolveTexture() ? VK_ATTACHMENT_STORE_OP_DONT_CARE : VK_ATTACHMENT_STORE_OP_STORE;
+ attDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+ attDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+ // this has to interact correctly with activateTextureRenderTarget(), hence leaving in COLOR_ATT
+ attDesc.initialLayout = preserveColor ? VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_UNDEFINED;
+ attDesc.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+ attDescs.append(attDesc);
+
+ const VkAttachmentReference ref = { uint32_t(attDescs.count() - 1), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
+ colorRefs.append(ref);
+ }
+
+ const bool hasDepthStencil = depthStencilBuffer || depthTexture;
+ if (hasDepthStencil) {
+ const VkFormat dsFormat = depthTexture ? QRHI_RES(QVkTexture, depthTexture)->vkformat
+ : QRHI_RES(QVkRenderBuffer, depthStencilBuffer)->vkformat;
+ const VkSampleCountFlagBits samples = depthTexture ? QRHI_RES(QVkTexture, depthTexture)->samples
+ : QRHI_RES(QVkRenderBuffer, depthStencilBuffer)->samples;
+ const VkAttachmentLoadOp loadOp = preserveDs ? VK_ATTACHMENT_LOAD_OP_LOAD : VK_ATTACHMENT_LOAD_OP_CLEAR;
+ const VkAttachmentStoreOp storeOp = depthTexture ? VK_ATTACHMENT_STORE_OP_STORE : VK_ATTACHMENT_STORE_OP_DONT_CARE;
+ VkAttachmentDescription attDesc;
+ memset(&attDesc, 0, sizeof(attDesc));
+ attDesc.format = dsFormat;
+ attDesc.samples = samples;
+ attDesc.loadOp = loadOp;
+ attDesc.storeOp = storeOp;
+ attDesc.stencilLoadOp = loadOp;
+ attDesc.stencilStoreOp = storeOp;
+ attDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ attDesc.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
+ attDescs.append(attDesc);
+ }
+ VkAttachmentReference dsRef = { uint32_t(attDescs.count() - 1), VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL };
+
+ for (int i = 0; i < colorAttCount; ++i) {
+ if (colorAttachments[i].resolveTexture()) {
+ QVkTexture *rtexD = QRHI_RES(QVkTexture, colorAttachments[i].resolveTexture());
+ if (rtexD->samples > VK_SAMPLE_COUNT_1_BIT)
+ qWarning("Resolving into a multisample texture is not supported");
+
+ VkAttachmentDescription attDesc;
+ memset(&attDesc, 0, sizeof(attDesc));
+ attDesc.format = rtexD->vkformat;
+ attDesc.samples = VK_SAMPLE_COUNT_1_BIT;
+ attDesc.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; // ignored
+ attDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
+ attDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+ attDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+ attDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ attDesc.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+ attDescs.append(attDesc);
+
+ const VkAttachmentReference ref = { uint32_t(attDescs.count() - 1), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
+ resolveRefs.append(ref);
+ } else {
+ const VkAttachmentReference ref = { VK_ATTACHMENT_UNUSED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
+ resolveRefs.append(ref);
+ }
+ }
+
+ VkSubpassDescription subpassDesc;
+ memset(&subpassDesc, 0, sizeof(subpassDesc));
+ subpassDesc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
+ subpassDesc.colorAttachmentCount = colorRefs.count();
+ Q_ASSERT(colorRefs.count() == resolveRefs.count());
+ subpassDesc.pColorAttachments = !colorRefs.isEmpty() ? colorRefs.constData() : nullptr;
+ subpassDesc.pDepthStencilAttachment = hasDepthStencil ? &dsRef : nullptr;
+ subpassDesc.pResolveAttachments = !resolveRefs.isEmpty() ? resolveRefs.constData() : nullptr;
+
+ VkRenderPassCreateInfo rpInfo;
+ memset(&rpInfo, 0, sizeof(rpInfo));
+ rpInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
+ rpInfo.attachmentCount = attDescs.count();
+ rpInfo.pAttachments = attDescs.constData();
+ rpInfo.subpassCount = 1;
+ rpInfo.pSubpasses = &subpassDesc;
+ // don't yet know the correct initial/final access and stage stuff for the
+ // implicit deps at this point, so leave it to the resource tracking to
+ // generate barriers
+
+ VkResult err = df->vkCreateRenderPass(dev, &rpInfo, nullptr, rp);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create renderpass: %d", err);
+ return false;
+ }
+
+ return true;
+}
+
+bool QRhiVulkan::recreateSwapChain(QRhiSwapChain *swapChain)
+{
+ QVkSwapChain *swapChainD = QRHI_RES(QVkSwapChain, swapChain);
+ if (swapChainD->pixelSize.isEmpty()) {
+ qWarning("Surface size is 0, cannot create swapchain");
+ return false;
+ }
+
+ df->vkDeviceWaitIdle(dev);
+
+ if (!vkCreateSwapchainKHR) {
+ vkCreateSwapchainKHR = reinterpret_cast<PFN_vkCreateSwapchainKHR>(f->vkGetDeviceProcAddr(dev, "vkCreateSwapchainKHR"));
+ vkDestroySwapchainKHR = reinterpret_cast<PFN_vkDestroySwapchainKHR>(f->vkGetDeviceProcAddr(dev, "vkDestroySwapchainKHR"));
+ vkGetSwapchainImagesKHR = reinterpret_cast<PFN_vkGetSwapchainImagesKHR>(f->vkGetDeviceProcAddr(dev, "vkGetSwapchainImagesKHR"));
+ vkAcquireNextImageKHR = reinterpret_cast<PFN_vkAcquireNextImageKHR>(f->vkGetDeviceProcAddr(dev, "vkAcquireNextImageKHR"));
+ vkQueuePresentKHR = reinterpret_cast<PFN_vkQueuePresentKHR>(f->vkGetDeviceProcAddr(dev, "vkQueuePresentKHR"));
+ if (!vkCreateSwapchainKHR || !vkDestroySwapchainKHR || !vkGetSwapchainImagesKHR || !vkAcquireNextImageKHR || !vkQueuePresentKHR) {
+ qWarning("Swapchain functions not available");
+ return false;
+ }
+ }
+
+ VkSurfaceCapabilitiesKHR surfaceCaps;
+ vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physDev, swapChainD->surface, &surfaceCaps);
+ quint32 reqBufferCount;
+ if (swapChainD->m_flags.testFlag(QRhiSwapChain::MinimalBufferCount)) {
+ reqBufferCount = qMax<quint32>(2, surfaceCaps.minImageCount);
+ } else {
+ const quint32 maxBuffers = QVkSwapChain::MAX_BUFFER_COUNT;
+ if (surfaceCaps.maxImageCount)
+ reqBufferCount = qMax(qMin(surfaceCaps.maxImageCount, maxBuffers), surfaceCaps.minImageCount);
+ else
+ reqBufferCount = qMax<quint32>(2, surfaceCaps.minImageCount);
+ }
+
+ VkSurfaceTransformFlagBitsKHR preTransform =
+ (surfaceCaps.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR)
+ ? VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR
+ : surfaceCaps.currentTransform;
+
+ VkCompositeAlphaFlagBitsKHR compositeAlpha =
+ (surfaceCaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR)
+ ? VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR
+ : VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
+
+ if (swapChainD->m_flags.testFlag(QRhiSwapChain::SurfaceHasPreMulAlpha)
+ && (surfaceCaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR))
+ {
+ compositeAlpha = VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR;
+ }
+
+ if (swapChainD->m_flags.testFlag(QRhiSwapChain::SurfaceHasNonPreMulAlpha)
+ && (surfaceCaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR))
+ {
+ compositeAlpha = VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR;
+ }
+
+ VkImageUsageFlags usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
+ swapChainD->supportsReadback = (surfaceCaps.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
+ if (swapChainD->supportsReadback && swapChainD->m_flags.testFlag(QRhiSwapChain::UsedAsTransferSource))
+ usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
+
+ VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR;
+ if (swapChainD->m_flags.testFlag(QRhiSwapChain::NoVSync)) {
+ if (swapChainD->supportedPresentationModes.contains(VK_PRESENT_MODE_MAILBOX_KHR))
+ presentMode = VK_PRESENT_MODE_MAILBOX_KHR;
+ else if (swapChainD->supportedPresentationModes.contains(VK_PRESENT_MODE_IMMEDIATE_KHR))
+ presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR;
+ }
+
+ // If the surface is different than before, then passing in the old
+ // swapchain associated with the old surface can fail the swapchain
+ // creation. (for example, Android loses the surface when backgrounding and
+ // restoring applications, and it also enforces failing swapchain creation
+ // with VK_ERROR_NATIVE_WINDOW_IN_USE_KHR if the old swapchain is provided)
+ const bool reuseExisting = swapChainD->sc && swapChainD->lastConnectedSurface == swapChainD->surface;
+
+ qDebug("Creating %s swapchain of %u buffers, size %dx%d, presentation mode %d",
+ reuseExisting ? "recycled" : "new",
+ reqBufferCount, swapChainD->pixelSize.width(), swapChainD->pixelSize.height(), presentMode);
+
+ VkSwapchainCreateInfoKHR swapChainInfo;
+ memset(&swapChainInfo, 0, sizeof(swapChainInfo));
+ swapChainInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
+ swapChainInfo.surface = swapChainD->surface;
+ swapChainInfo.minImageCount = reqBufferCount;
+ swapChainInfo.imageFormat = swapChainD->colorFormat;
+ swapChainInfo.imageColorSpace = swapChainD->colorSpace;
+ swapChainInfo.imageExtent = VkExtent2D { uint32_t(swapChainD->pixelSize.width()), uint32_t(swapChainD->pixelSize.height()) };
+ swapChainInfo.imageArrayLayers = 1;
+ swapChainInfo.imageUsage = usage;
+ swapChainInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
+ swapChainInfo.preTransform = preTransform;
+ swapChainInfo.compositeAlpha = compositeAlpha;
+ swapChainInfo.presentMode = presentMode;
+ swapChainInfo.clipped = true;
+ swapChainInfo.oldSwapchain = reuseExisting ? swapChainD->sc : VK_NULL_HANDLE;
+
+ VkSwapchainKHR newSwapChain;
+ VkResult err = vkCreateSwapchainKHR(dev, &swapChainInfo, nullptr, &newSwapChain);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create swapchain: %d", err);
+ return false;
+ }
+
+ if (swapChainD->sc)
+ releaseSwapChainResources(swapChain);
+
+ swapChainD->sc = newSwapChain;
+ swapChainD->lastConnectedSurface = swapChainD->surface;
+
+ quint32 actualSwapChainBufferCount = 0;
+ err = vkGetSwapchainImagesKHR(dev, swapChainD->sc, &actualSwapChainBufferCount, nullptr);
+ if (err != VK_SUCCESS || actualSwapChainBufferCount < 2) {
+ qWarning("Failed to get swapchain images: %d (count=%u)", err, actualSwapChainBufferCount);
+ return false;
+ }
+
+ if (actualSwapChainBufferCount > QVkSwapChain::MAX_BUFFER_COUNT) {
+ qWarning("Too many swapchain buffers (%u)", actualSwapChainBufferCount);
+ return false;
+ }
+ if (actualSwapChainBufferCount != reqBufferCount)
+ qDebug("Actual swapchain buffer count is %u", actualSwapChainBufferCount);
+ swapChainD->bufferCount = actualSwapChainBufferCount;
+
+ VkImage swapChainImages[QVkSwapChain::MAX_BUFFER_COUNT];
+ err = vkGetSwapchainImagesKHR(dev, swapChainD->sc, &actualSwapChainBufferCount, swapChainImages);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to get swapchain images: %d", err);
+ return false;
+ }
+
+ VkImage msaaImages[QVkSwapChain::MAX_BUFFER_COUNT];
+ VkImageView msaaViews[QVkSwapChain::MAX_BUFFER_COUNT];
+ if (swapChainD->samples > VK_SAMPLE_COUNT_1_BIT) {
+ if (!createTransientImage(swapChainD->colorFormat,
+ swapChainD->pixelSize,
+ VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
+ VK_IMAGE_ASPECT_COLOR_BIT,
+ swapChainD->samples,
+ &swapChainD->msaaImageMem,
+ msaaImages,
+ msaaViews,
+ swapChainD->bufferCount))
+ {
+ qWarning("Failed to create transient image for MSAA color buffer");
+ return false;
+ }
+ }
+
+ VkFenceCreateInfo fenceInfo;
+ memset(&fenceInfo, 0, sizeof(fenceInfo));
+ fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
+ fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
+
+ for (int i = 0; i < swapChainD->bufferCount; ++i) {
+ QVkSwapChain::ImageResources &image(swapChainD->imageRes[i]);
+ image.image = swapChainImages[i];
+ if (swapChainD->samples > VK_SAMPLE_COUNT_1_BIT) {
+ image.msaaImage = msaaImages[i];
+ image.msaaImageView = msaaViews[i];
+ }
+
+ VkImageViewCreateInfo imgViewInfo;
+ memset(&imgViewInfo, 0, sizeof(imgViewInfo));
+ imgViewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
+ imgViewInfo.image = swapChainImages[i];
+ imgViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
+ imgViewInfo.format = swapChainD->colorFormat;
+ imgViewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
+ imgViewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
+ imgViewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
+ imgViewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
+ imgViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ imgViewInfo.subresourceRange.levelCount = imgViewInfo.subresourceRange.layerCount = 1;
+ err = df->vkCreateImageView(dev, &imgViewInfo, nullptr, &image.imageView);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create swapchain image view %d: %d", i, err);
+ return false;
+ }
+ }
+
+ swapChainD->currentImageIndex = 0;
+
+ VkSemaphoreCreateInfo semInfo;
+ memset(&semInfo, 0, sizeof(semInfo));
+ semInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
+
+ for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
+ QVkSwapChain::FrameResources &frame(swapChainD->frameRes[i]);
+
+ frame.imageAcquired = false;
+ frame.imageSemWaitable = false;
+
+ df->vkCreateFence(dev, &fenceInfo, nullptr, &frame.imageFence);
+ frame.imageFenceWaitable = true; // fence was created in signaled state
+
+ df->vkCreateSemaphore(dev, &semInfo, nullptr, &frame.imageSem);
+ df->vkCreateSemaphore(dev, &semInfo, nullptr, &frame.drawSem);
+
+ err = df->vkCreateFence(dev, &fenceInfo, nullptr, &frame.cmdFence);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create command buffer fence: %d", err);
+ return false;
+ }
+ frame.cmdFenceWaitable = true; // fence was created in signaled state
+ }
+
+ swapChainD->currentFrameSlot = 0;
+
+ return true;
+}
+
+void QRhiVulkan::releaseSwapChainResources(QRhiSwapChain *swapChain)
+{
+ QVkSwapChain *swapChainD = QRHI_RES(QVkSwapChain, swapChain);
+
+ if (swapChainD->sc == VK_NULL_HANDLE)
+ return;
+
+ df->vkDeviceWaitIdle(dev);
+
+ for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
+ QVkSwapChain::FrameResources &frame(swapChainD->frameRes[i]);
+ if (frame.cmdFence) {
+ if (frame.cmdFenceWaitable)
+ df->vkWaitForFences(dev, 1, &frame.cmdFence, VK_TRUE, UINT64_MAX);
+ df->vkDestroyFence(dev, frame.cmdFence, nullptr);
+ frame.cmdFence = VK_NULL_HANDLE;
+ frame.cmdFenceWaitable = false;
+ }
+ if (frame.imageFence) {
+ if (frame.imageFenceWaitable)
+ df->vkWaitForFences(dev, 1, &frame.imageFence, VK_TRUE, UINT64_MAX);
+ df->vkDestroyFence(dev, frame.imageFence, nullptr);
+ frame.imageFence = VK_NULL_HANDLE;
+ frame.imageFenceWaitable = false;
+ }
+ if (frame.imageSem) {
+ df->vkDestroySemaphore(dev, frame.imageSem, nullptr);
+ frame.imageSem = VK_NULL_HANDLE;
+ }
+ if (frame.drawSem) {
+ df->vkDestroySemaphore(dev, frame.drawSem, nullptr);
+ frame.drawSem = VK_NULL_HANDLE;
+ }
+ if (frame.cmdBuf) {
+ df->vkFreeCommandBuffers(dev, cmdPool, 1, &frame.cmdBuf);
+ frame.cmdBuf = VK_NULL_HANDLE;
+ }
+ }
+
+ for (int i = 0; i < swapChainD->bufferCount; ++i) {
+ QVkSwapChain::ImageResources &image(swapChainD->imageRes[i]);
+ if (image.fb) {
+ df->vkDestroyFramebuffer(dev, image.fb, nullptr);
+ image.fb = VK_NULL_HANDLE;
+ }
+ if (image.imageView) {
+ df->vkDestroyImageView(dev, image.imageView, nullptr);
+ image.imageView = VK_NULL_HANDLE;
+ }
+ if (image.msaaImageView) {
+ df->vkDestroyImageView(dev, image.msaaImageView, nullptr);
+ image.msaaImageView = VK_NULL_HANDLE;
+ }
+ if (image.msaaImage) {
+ df->vkDestroyImage(dev, image.msaaImage, nullptr);
+ image.msaaImage = VK_NULL_HANDLE;
+ }
+ }
+
+ if (swapChainD->msaaImageMem) {
+ df->vkFreeMemory(dev, swapChainD->msaaImageMem, nullptr);
+ swapChainD->msaaImageMem = VK_NULL_HANDLE;
+ }
+
+ vkDestroySwapchainKHR(dev, swapChainD->sc, nullptr);
+ swapChainD->sc = VK_NULL_HANDLE;
+
+ // NB! surface and similar must remain intact
+}
+
+static inline bool checkDeviceLost(VkResult err)
+{
+ if (err == VK_ERROR_DEVICE_LOST) {
+ qWarning("Device lost");
+ return true;
+ }
+ return false;
+}
+
+QRhi::FrameOpResult QRhiVulkan::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags)
+{
+ Q_UNUSED(flags);
+ QVkSwapChain *swapChainD = QRHI_RES(QVkSwapChain, swapChain);
+ QVkSwapChain::FrameResources &frame(swapChainD->frameRes[swapChainD->currentFrameSlot]);
+ QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
+
+ if (!frame.imageAcquired) {
+ // Wait if we are too far ahead, i.e. the thread gets throttled based on the presentation rate
+ // (note that we are using FIFO mode -> vsync)
+ if (frame.imageFenceWaitable) {
+ df->vkWaitForFences(dev, 1, &frame.imageFence, VK_TRUE, UINT64_MAX);
+ df->vkResetFences(dev, 1, &frame.imageFence);
+ frame.imageFenceWaitable = false;
+ }
+
+ // move on to next swapchain image
+ VkResult err = vkAcquireNextImageKHR(dev, swapChainD->sc, UINT64_MAX,
+ frame.imageSem, frame.imageFence, &frame.imageIndex);
+ if (err == VK_SUCCESS || err == VK_SUBOPTIMAL_KHR) {
+ swapChainD->currentImageIndex = frame.imageIndex;
+ frame.imageSemWaitable = true;
+ frame.imageAcquired = true;
+ frame.imageFenceWaitable = true;
+ } else if (err == VK_ERROR_OUT_OF_DATE_KHR) {
+ return QRhi::FrameOpSwapChainOutOfDate;
+ } else {
+ if (checkDeviceLost(err))
+ return QRhi::FrameOpDeviceLost;
+ else
+ qWarning("Failed to acquire next swapchain image: %d", err);
+ return QRhi::FrameOpError;
+ }
+ }
+
+ // Make sure the previous commands for the same image have finished. (note
+ // that this is based on the fence from the command buffer submit, nothing
+ // to do with the Present)
+ //
+ // Do this also for any other swapchain's commands with the same frame slot
+ // While this reduces concurrency, it keeps resource usage safe: swapchain
+ // A starting its frame 0, followed by swapchain B starting its own frame 0
+ // will make B wait for A's frame 0 commands, so if a resource is written
+ // in B's frame or when B checks for pending resource releases, that won't
+ // mess up A's in-flight commands (as they are not in flight anymore).
+ waitCommandCompletion(swapChainD->currentFrameSlot);
+
+ // Now is the time to read the timestamps for the previous frame for this slot.
+ if (frame.timestampQueryIndex >= 0) {
+ quint64 timestamp[2] = { 0, 0 };
+ VkResult err = df->vkGetQueryPoolResults(dev, timestampQueryPool, frame.timestampQueryIndex, 2,
+ 2 * sizeof(quint64), timestamp, sizeof(quint64), VK_QUERY_RESULT_64_BIT);
+ timestampQueryPoolMap.clearBit(frame.timestampQueryIndex / 2);
+ frame.timestampQueryIndex = -1;
+ if (err == VK_SUCCESS) {
+ quint64 mask = 0;
+ for (quint64 i = 0; i < timestampValidBits; i += 8)
+ mask |= 0xFFULL << i;
+ const quint64 ts0 = timestamp[0] & mask;
+ const quint64 ts1 = timestamp[1] & mask;
+ const float nsecsPerTick = physDevProperties.limits.timestampPeriod;
+ if (!qFuzzyIsNull(nsecsPerTick)) {
+ const float elapsedMs = float(ts1 - ts0) * nsecsPerTick / 1000000.0f;
+ // now we have the gpu time for the previous frame for this slot, report it
+ // (does not matter that it is not for this frame)
+ QRHI_PROF_F(swapChainFrameGpuTime(swapChain, elapsedMs));
+ }
+ } else {
+ qWarning("Failed to query timestamp: %d", err);
+ }
+ }
+
+ // build new draw command buffer
+ QRhi::FrameOpResult cbres = startCommandBuffer(&frame.cmdBuf);
+ if (cbres != QRhi::FrameOpSuccess)
+ return cbres;
+
+ // when profiling is enabled, pick a free query (pair) from the pool
+ int timestampQueryIdx = -1;
+ if (profilerPrivateOrNull()) {
+ for (int i = 0; i < timestampQueryPoolMap.count(); ++i) {
+ if (!timestampQueryPoolMap.testBit(i)) {
+ timestampQueryPoolMap.setBit(i);
+ timestampQueryIdx = i * 2;
+ break;
+ }
+ }
+ }
+ if (timestampQueryIdx >= 0) {
+ df->vkCmdResetQueryPool(frame.cmdBuf, timestampQueryPool, timestampQueryIdx, 2);
+ // record timestamp at the start of the command buffer
+ df->vkCmdWriteTimestamp(frame.cmdBuf, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
+ timestampQueryPool, timestampQueryIdx);
+ frame.timestampQueryIndex = timestampQueryIdx;
+ }
+
+ swapChainD->cbWrapper.cb = frame.cmdBuf;
+ QVkSwapChain::ImageResources &image(swapChainD->imageRes[swapChainD->currentImageIndex]);
+ swapChainD->rtWrapper.d.fb = image.fb;
+
+ currentFrameSlot = swapChainD->currentFrameSlot;
+ currentSwapChain = swapChainD;
+ if (swapChainD->ds)
+ swapChainD->ds->lastActiveFrameSlot = currentFrameSlot;
+
+ QRHI_PROF_F(beginSwapChainFrame(swapChain));
+
+ prepareNewFrame(&swapChainD->cbWrapper);
+
+ return QRhi::FrameOpSuccess;
+}
+
+QRhi::FrameOpResult QRhiVulkan::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags)
+{
+ Q_ASSERT(inFrame);
+ inFrame = false;
+
+ QVkSwapChain *swapChainD = QRHI_RES(QVkSwapChain, swapChain);
+ Q_ASSERT(currentSwapChain == swapChainD);
+
+ recordCommandBuffer(&swapChainD->cbWrapper);
+
+ QVkSwapChain::FrameResources &frame(swapChainD->frameRes[swapChainD->currentFrameSlot]);
+ QVkSwapChain::ImageResources &image(swapChainD->imageRes[swapChainD->currentImageIndex]);
+
+ if (image.transferSource) {
+ // was used in a readback as transfer source, go back to presentable layout
+ VkImageMemoryBarrier presTrans;
+ memset(&presTrans, 0, sizeof(presTrans));
+ presTrans.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
+ presTrans.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
+ presTrans.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+ presTrans.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
+ presTrans.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
+ presTrans.image = image.image;
+ presTrans.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ presTrans.subresourceRange.levelCount = presTrans.subresourceRange.layerCount = 1;
+ df->vkCmdPipelineBarrier(frame.cmdBuf,
+ VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
+ 0, 0, nullptr, 0, nullptr,
+ 1, &presTrans);
+ image.transferSource = false;
+ }
+
+ // record another timestamp, when enabled
+ if (frame.timestampQueryIndex >= 0) {
+ df->vkCmdWriteTimestamp(frame.cmdBuf, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
+ timestampQueryPool, frame.timestampQueryIndex + 1);
+ }
+
+ // stop recording and submit to the queue
+ Q_ASSERT(!frame.cmdFenceWaitable);
+ const bool needsPresent = !flags.testFlag(QRhi::SkipPresent);
+ QRhi::FrameOpResult submitres = endAndSubmitCommandBuffer(frame.cmdBuf,
+ frame.cmdFence,
+ frame.imageSemWaitable ? &frame.imageSem : nullptr,
+ needsPresent ? &frame.drawSem : nullptr);
+ if (submitres != QRhi::FrameOpSuccess)
+ return submitres;
+
+ frame.imageSemWaitable = false;
+ frame.cmdFenceWaitable = true;
+
+ QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
+ // this must be done before the Present
+ QRHI_PROF_F(endSwapChainFrame(swapChain, swapChainD->frameCount + 1));
+
+ if (needsPresent) {
+ // add the Present to the queue
+ VkPresentInfoKHR presInfo;
+ memset(&presInfo, 0, sizeof(presInfo));
+ presInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
+ presInfo.swapchainCount = 1;
+ presInfo.pSwapchains = &swapChainD->sc;
+ presInfo.pImageIndices = &swapChainD->currentImageIndex;
+ presInfo.waitSemaphoreCount = 1;
+ presInfo.pWaitSemaphores = &frame.drawSem; // gfxQueueFamilyIdx == presQueueFamilyIdx ? &frame.drawSem : &frame.presTransSem;
+
+ VkResult err = vkQueuePresentKHR(gfxQueue, &presInfo);
+ if (err != VK_SUCCESS) {
+ if (err == VK_ERROR_OUT_OF_DATE_KHR) {
+ return QRhi::FrameOpSwapChainOutOfDate;
+ } else if (err != VK_SUBOPTIMAL_KHR) {
+ if (checkDeviceLost(err))
+ return QRhi::FrameOpDeviceLost;
+ else
+ qWarning("Failed to present: %d", err);
+ return QRhi::FrameOpError;
+ }
+ }
+
+ // mark the current swapchain buffer as unused from our side
+ frame.imageAcquired = false;
+ // and move on to the next buffer
+ swapChainD->currentFrameSlot = (swapChainD->currentFrameSlot + 1) % QVK_FRAMES_IN_FLIGHT;
+ }
+
+ swapChainD->frameCount += 1;
+ currentSwapChain = nullptr;
+ return QRhi::FrameOpSuccess;
+}
+
+void QRhiVulkan::prepareNewFrame(QRhiCommandBuffer *cb)
+{
+ Q_ASSERT(!inFrame);
+ inFrame = true;
+
+ // Now is the time to do things for frame N-F, where N is the current one,
+ // F is QVK_FRAMES_IN_FLIGHT, because only here it is guaranteed that that
+ // frame has completed on the GPU (due to the fence wait in beginFrame). To
+ // decide if something is safe to handle now a simple "lastActiveFrameSlot
+ // == currentFrameSlot" is sufficient (remember that e.g. with F==2
+ // currentFrameSlot goes 0, 1, 0, 1, 0, ...)
+ //
+ // With multiple swapchains on the same QRhi things get more convoluted
+ // (and currentFrameSlot strictly alternating is not true anymore) but
+ // beginNonWrapperFrame() solves that by blocking as necessary so the rest
+ // here is safe regardless.
+
+ executeDeferredReleases();
+
+ QRHI_RES(QVkCommandBuffer, cb)->resetState();
+
+ finishActiveReadbacks(); // last, in case the readback-completed callback issues rhi calls
+}
+
+QRhi::FrameOpResult QRhiVulkan::startCommandBuffer(VkCommandBuffer *cb)
+{
+ if (*cb) {
+ df->vkFreeCommandBuffers(dev, cmdPool, 1, cb);
+ *cb = VK_NULL_HANDLE;
+ }
+
+ VkCommandBufferAllocateInfo cmdBufInfo;
+ memset(&cmdBufInfo, 0, sizeof(cmdBufInfo));
+ cmdBufInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
+ cmdBufInfo.commandPool = cmdPool;
+ cmdBufInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
+ cmdBufInfo.commandBufferCount = 1;
+
+ VkResult err = df->vkAllocateCommandBuffers(dev, &cmdBufInfo, cb);
+ if (err != VK_SUCCESS) {
+ if (checkDeviceLost(err))
+ return QRhi::FrameOpDeviceLost;
+ else
+ qWarning("Failed to allocate frame command buffer: %d", err);
+ return QRhi::FrameOpError;
+ }
+
+ VkCommandBufferBeginInfo cmdBufBeginInfo;
+ memset(&cmdBufBeginInfo, 0, sizeof(cmdBufBeginInfo));
+ cmdBufBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
+
+ err = df->vkBeginCommandBuffer(*cb, &cmdBufBeginInfo);
+ if (err != VK_SUCCESS) {
+ if (checkDeviceLost(err))
+ return QRhi::FrameOpDeviceLost;
+ else
+ qWarning("Failed to begin frame command buffer: %d", err);
+ return QRhi::FrameOpError;
+ }
+
+ return QRhi::FrameOpSuccess;
+}
+
+QRhi::FrameOpResult QRhiVulkan::endAndSubmitCommandBuffer(VkCommandBuffer cb, VkFence cmdFence,
+ VkSemaphore *waitSem, VkSemaphore *signalSem)
+{
+ VkResult err = df->vkEndCommandBuffer(cb);
+ if (err != VK_SUCCESS) {
+ if (checkDeviceLost(err))
+ return QRhi::FrameOpDeviceLost;
+ else
+ qWarning("Failed to end frame command buffer: %d", err);
+ return QRhi::FrameOpError;
+ }
+
+ VkSubmitInfo submitInfo;
+ memset(&submitInfo, 0, sizeof(submitInfo));
+ submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
+ submitInfo.commandBufferCount = 1;
+ submitInfo.pCommandBuffers = &cb;
+ if (waitSem) {
+ submitInfo.waitSemaphoreCount = 1;
+ submitInfo.pWaitSemaphores = waitSem;
+ }
+ if (signalSem) {
+ submitInfo.signalSemaphoreCount = 1;
+ submitInfo.pSignalSemaphores = signalSem;
+ }
+ VkPipelineStageFlags psf = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+ submitInfo.pWaitDstStageMask = &psf;
+
+ err = df->vkQueueSubmit(gfxQueue, 1, &submitInfo, cmdFence);
+ if (err != VK_SUCCESS) {
+ if (checkDeviceLost(err))
+ return QRhi::FrameOpDeviceLost;
+ else
+ qWarning("Failed to submit to graphics queue: %d", err);
+ return QRhi::FrameOpError;
+ }
+
+ return QRhi::FrameOpSuccess;
+}
+
+void QRhiVulkan::waitCommandCompletion(int frameSlot)
+{
+ for (QVkSwapChain *sc : qAsConst(swapchains)) {
+ QVkSwapChain::FrameResources &frame(sc->frameRes[frameSlot]);
+ if (frame.cmdFenceWaitable) {
+ df->vkWaitForFences(dev, 1, &frame.cmdFence, VK_TRUE, UINT64_MAX);
+ df->vkResetFences(dev, 1, &frame.cmdFence);
+ frame.cmdFenceWaitable = false;
+ }
+ }
+}
+
+QRhi::FrameOpResult QRhiVulkan::beginOffscreenFrame(QRhiCommandBuffer **cb)
+{
+ QRhi::FrameOpResult cbres = startCommandBuffer(&ofr.cbWrapper.cb);
+ if (cbres != QRhi::FrameOpSuccess)
+ return cbres;
+
+ // Switch to the next slot manually. Swapchains do not know about this
+ // which is good. So for example a - unusual but possible - onscreen,
+ // onscreen, offscreen, onscreen, onscreen, onscreen sequence of
+ // begin/endFrame leads to 0, 1, 0, 0, 1, 0. This works because the
+ // offscreen frame is synchronous in the sense that we wait for execution
+ // to complete in endFrame, and so no resources used in that frame are busy
+ // anymore in the next frame.
+ currentFrameSlot = (currentFrameSlot + 1) % QVK_FRAMES_IN_FLIGHT;
+ // except that this gets complicated with multiple swapchains so make sure
+ // any pending commands have finished for the frame slot we are going to use
+ if (swapchains.count() > 1)
+ waitCommandCompletion(currentFrameSlot);
+
+ prepareNewFrame(&ofr.cbWrapper);
+ ofr.active = true;
+
+ *cb = &ofr.cbWrapper;
+ return QRhi::FrameOpSuccess;
+}
+
+QRhi::FrameOpResult QRhiVulkan::endOffscreenFrame()
+{
+ Q_ASSERT(inFrame);
+ inFrame = false;
+ Q_ASSERT(ofr.active);
+ ofr.active = false;
+
+ recordCommandBuffer(&ofr.cbWrapper);
+
+ if (!ofr.cmdFence) {
+ VkFenceCreateInfo fenceInfo;
+ memset(&fenceInfo, 0, sizeof(fenceInfo));
+ fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
+ VkResult err = df->vkCreateFence(dev, &fenceInfo, nullptr, &ofr.cmdFence);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create command buffer fence: %d", err);
+ return QRhi::FrameOpError;
+ }
+ }
+
+ QRhi::FrameOpResult submitres = endAndSubmitCommandBuffer(ofr.cbWrapper.cb, ofr.cmdFence, nullptr, nullptr);
+ if (submitres != QRhi::FrameOpSuccess)
+ return submitres;
+
+ // wait for completion
+ df->vkWaitForFences(dev, 1, &ofr.cmdFence, VK_TRUE, UINT64_MAX);
+ df->vkResetFences(dev, 1, &ofr.cmdFence);
+
+ // Here we know that executing the host-side reads for this (or any
+ // previous) frame is safe since we waited for completion above.
+ finishActiveReadbacks(true);
+
+ return QRhi::FrameOpSuccess;
+}
+
+QRhi::FrameOpResult QRhiVulkan::finish()
+{
+ Q_ASSERT(!inPass);
+
+ QVkSwapChain *swapChainD = nullptr;
+ if (inFrame) {
+ // There is either a swapchain or an offscreen frame on-going.
+ // End command recording and submit what we have.
+ VkCommandBuffer cb;
+ if (ofr.active) {
+ Q_ASSERT(!currentSwapChain);
+ recordCommandBuffer(&ofr.cbWrapper);
+ cb = ofr.cbWrapper.cb;
+ } else {
+ Q_ASSERT(currentSwapChain);
+ swapChainD = currentSwapChain;
+ recordCommandBuffer(&swapChainD->cbWrapper);
+ cb = swapChainD->cbWrapper.cb;
+ }
+ QRhi::FrameOpResult submitres = endAndSubmitCommandBuffer(cb, VK_NULL_HANDLE, nullptr, nullptr);
+ if (submitres != QRhi::FrameOpSuccess)
+ return submitres;
+ }
+
+ df->vkQueueWaitIdle(gfxQueue);
+
+ if (inFrame) {
+ // Allocate and begin recording on a new command buffer.
+ if (ofr.active)
+ startCommandBuffer(&ofr.cbWrapper.cb);
+ else
+ startCommandBuffer(&swapChainD->frameRes[swapChainD->currentFrameSlot].cmdBuf);
+ }
+
+ executeDeferredReleases(true);
+ finishActiveReadbacks(true);
+
+ return QRhi::FrameOpSuccess;
+}
+
+static inline QRhiPassResourceTracker::UsageState toPassTrackerUsageState(const QVkBuffer::UsageState &bufUsage)
+{
+ QRhiPassResourceTracker::UsageState u;
+ u.layout = 0; // unused with buffers
+ u.access = bufUsage.access;
+ u.stage = bufUsage.stage;
+ return u;
+}
+
+static inline QRhiPassResourceTracker::UsageState toPassTrackerUsageState(const QVkTexture::UsageState &texUsage)
+{
+ QRhiPassResourceTracker::UsageState u;
+ u.layout = texUsage.layout;
+ u.access = texUsage.access;
+ u.stage = texUsage.stage;
+ return u;
+}
+
+void QRhiVulkan::activateTextureRenderTarget(QVkCommandBuffer *cbD, QVkTextureRenderTarget *rtD)
+{
+ rtD->lastActiveFrameSlot = currentFrameSlot;
+ rtD->d.rp->lastActiveFrameSlot = currentFrameSlot;
+ QRhiPassResourceTracker &passResTracker(cbD->passResTrackers[cbD->currentPassResTrackerIndex]);
+ const QVector<QRhiColorAttachment> colorAttachments = rtD->m_desc.colorAttachments();
+ for (const QRhiColorAttachment &colorAttachment : colorAttachments) {
+ QVkTexture *texD = QRHI_RES(QVkTexture, colorAttachment.texture());
+ QVkTexture *resolveTexD = QRHI_RES(QVkTexture, colorAttachment.resolveTexture());
+ QVkRenderBuffer *rbD = QRHI_RES(QVkRenderBuffer, colorAttachment.renderBuffer());
+ if (texD) {
+ trackedRegisterTexture(&passResTracker, texD,
+ QRhiPassResourceTracker::TexColorOutput,
+ QRhiPassResourceTracker::TexColorOutputStage);
+ texD->lastActiveFrameSlot = currentFrameSlot;
+ } else if (rbD) {
+ // Won't register rbD->backingTexture because it cannot be used for
+ // anything in a renderpass, its use makes only sense in
+ // combination with a resolveTexture.
+ rbD->lastActiveFrameSlot = currentFrameSlot;
+ }
+ if (resolveTexD) {
+ trackedRegisterTexture(&passResTracker, resolveTexD,
+ QRhiPassResourceTracker::TexColorOutput,
+ QRhiPassResourceTracker::TexColorOutputStage);
+ resolveTexD->lastActiveFrameSlot = currentFrameSlot;
+ }
+ }
+ if (rtD->m_desc.depthStencilBuffer())
+ QRHI_RES(QVkRenderBuffer, rtD->m_desc.depthStencilBuffer())->lastActiveFrameSlot = currentFrameSlot;
+ if (rtD->m_desc.depthTexture()) {
+ QVkTexture *depthTexD = QRHI_RES(QVkTexture, rtD->m_desc.depthTexture());
+ trackedRegisterTexture(&passResTracker, depthTexD,
+ QRhiPassResourceTracker::TexDepthOutput,
+ QRhiPassResourceTracker::TexDepthOutputStage);
+ depthTexD->lastActiveFrameSlot = currentFrameSlot;
+ }
+}
+
+void QRhiVulkan::resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ Q_ASSERT(inFrame && !inPass);
+
+ enqueueResourceUpdates(QRHI_RES(QVkCommandBuffer, cb), resourceUpdates);
+}
+
+void QRhiVulkan::beginPass(QRhiCommandBuffer *cb,
+ QRhiRenderTarget *rt,
+ const QColor &colorClearValue,
+ const QRhiDepthStencilClearValue &depthStencilClearValue,
+ QRhiResourceUpdateBatch *resourceUpdates)
+{
+ Q_ASSERT(inFrame && !inPass);
+ QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
+
+ if (resourceUpdates)
+ enqueueResourceUpdates(cbD, resourceUpdates);
+
+ // Insert a TransitionPassResources into the command stream, pointing to
+ // the tracker this pass is going to use. That's how we generate the
+ // barriers later during recording the real VkCommandBuffer, right before
+ // the vkCmdBeginRenderPass.
+ enqueueTransitionPassResources(cbD);
+
+ QVkRenderTargetData *rtD = nullptr;
+ switch (rt->resourceType()) {
+ case QRhiResource::RenderTarget:
+ rtD = &QRHI_RES(QVkReferenceRenderTarget, rt)->d;
+ rtD->rp->lastActiveFrameSlot = currentFrameSlot;
+ break;
+ case QRhiResource::TextureRenderTarget:
+ {
+ QVkTextureRenderTarget *rtTex = QRHI_RES(QVkTextureRenderTarget, rt);
+ rtD = &rtTex->d;
+ activateTextureRenderTarget(cbD, rtTex);
+ }
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+
+ cbD->currentTarget = rt;
+
+ // No copy operations or image layout transitions allowed after this point
+ // (up until endPass) as we are going to begin the renderpass.
+
+ VkRenderPassBeginInfo rpBeginInfo;
+ memset(&rpBeginInfo, 0, sizeof(rpBeginInfo));
+ rpBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
+ rpBeginInfo.renderPass = rtD->rp->rp;
+ rpBeginInfo.framebuffer = rtD->fb;
+ rpBeginInfo.renderArea.extent.width = rtD->pixelSize.width();
+ rpBeginInfo.renderArea.extent.height = rtD->pixelSize.height();
+
+ QVarLengthArray<VkClearValue, 4> cvs;
+ for (int i = 0; i < rtD->colorAttCount; ++i) {
+ VkClearValue cv;
+ cv.color = { { float(colorClearValue.redF()), float(colorClearValue.greenF()), float(colorClearValue.blueF()),
+ float(colorClearValue.alphaF()) } };
+ cvs.append(cv);
+ }
+ for (int i = 0; i < rtD->dsAttCount; ++i) {
+ VkClearValue cv;
+ cv.depthStencil = { depthStencilClearValue.depthClearValue(), depthStencilClearValue.stencilClearValue() };
+ cvs.append(cv);
+ }
+ for (int i = 0; i < rtD->resolveAttCount; ++i) {
+ VkClearValue cv;
+ cv.color = { { float(colorClearValue.redF()), float(colorClearValue.greenF()), float(colorClearValue.blueF()),
+ float(colorClearValue.alphaF()) } };
+ cvs.append(cv);
+ }
+ rpBeginInfo.clearValueCount = cvs.count();
+
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::BeginRenderPass;
+ cmd.args.beginRenderPass.desc = rpBeginInfo;
+ cmd.args.beginRenderPass.clearValueIndex = cbD->pools.clearValue.count();
+ cbD->pools.clearValue.append(cvs.constData(), cvs.count());
+ cbD->commands.append(cmd);
+
+ inPass = true;
+}
+
+void QRhiVulkan::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ Q_ASSERT(inPass);
+ QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
+
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::EndRenderPass;
+ cbD->commands.append(cmd);
+
+ inPass = false;
+ cbD->currentTarget = nullptr;
+
+ if (resourceUpdates)
+ enqueueResourceUpdates(cbD, resourceUpdates);
+}
+
+VkShaderModule QRhiVulkan::createShader(const QByteArray &spirv)
+{
+ VkShaderModuleCreateInfo shaderInfo;
+ memset(&shaderInfo, 0, sizeof(shaderInfo));
+ shaderInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
+ shaderInfo.codeSize = spirv.size();
+ shaderInfo.pCode = reinterpret_cast<const quint32 *>(spirv.constData());
+ VkShaderModule shaderModule;
+ VkResult err = df->vkCreateShaderModule(dev, &shaderInfo, nullptr, &shaderModule);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create shader module: %d", err);
+ return VK_NULL_HANDLE;
+ }
+ return shaderModule;
+}
+
+bool QRhiVulkan::ensurePipelineCache()
+{
+ if (pipelineCache)
+ return true;
+
+ VkPipelineCacheCreateInfo pipelineCacheInfo;
+ memset(&pipelineCacheInfo, 0, sizeof(pipelineCacheInfo));
+ pipelineCacheInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
+ VkResult err = df->vkCreatePipelineCache(dev, &pipelineCacheInfo, nullptr, &pipelineCache);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create pipeline cache: %d", err);
+ return false;
+ }
+ return true;
+}
+
+void QRhiVulkan::updateShaderResourceBindings(QRhiShaderResourceBindings *srb, int descSetIdx)
+{
+ QVkShaderResourceBindings *srbD = QRHI_RES(QVkShaderResourceBindings, srb);
+
+ QVarLengthArray<VkDescriptorBufferInfo, 4> bufferInfos;
+ QVarLengthArray<VkDescriptorImageInfo, 4> imageInfos;
+ QVarLengthArray<VkWriteDescriptorSet, 8> writeInfos;
+
+ const bool updateAll = descSetIdx < 0;
+ int frameSlot = updateAll ? 0 : descSetIdx;
+ while (frameSlot < (updateAll ? QVK_FRAMES_IN_FLIGHT : descSetIdx + 1)) {
+ srbD->boundResourceData[frameSlot].resize(srbD->sortedBindings.count());
+ for (int i = 0, ie = srbD->sortedBindings.count(); i != ie; ++i) {
+ const QRhiShaderResourceBindingPrivate *b = QRhiShaderResourceBindingPrivate::get(&srbD->sortedBindings[i]);
+ QVkShaderResourceBindings::BoundResourceData &bd(srbD->boundResourceData[frameSlot][i]);
+
+ VkWriteDescriptorSet writeInfo;
+ memset(&writeInfo, 0, sizeof(writeInfo));
+ writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ writeInfo.dstSet = srbD->descSets[frameSlot];
+ writeInfo.dstBinding = b->binding;
+ writeInfo.descriptorCount = 1;
+
+ switch (b->type) {
+ case QRhiShaderResourceBinding::UniformBuffer:
+ {
+ writeInfo.descriptorType = b->u.ubuf.hasDynamicOffset ? VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC
+ : VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+ QRhiBuffer *buf = b->u.ubuf.buf;
+ QVkBuffer *bufD = QRHI_RES(QVkBuffer, buf);
+ bd.ubuf.id = bufD->m_id;
+ bd.ubuf.generation = bufD->generation;
+ VkDescriptorBufferInfo bufInfo;
+ bufInfo.buffer = bufD->m_type == QRhiBuffer::Dynamic ? bufD->buffers[frameSlot] : bufD->buffers[0];
+ bufInfo.offset = b->u.ubuf.offset;
+ bufInfo.range = b->u.ubuf.maybeSize ? b->u.ubuf.maybeSize : bufD->m_size;
+ // be nice and assert when we know the vulkan device would die a horrible death due to non-aligned reads
+ Q_ASSERT(aligned(bufInfo.offset, ubufAlign) == bufInfo.offset);
+ bufferInfos.append(bufInfo);
+ writeInfo.pBufferInfo = &bufferInfos.last();
+ }
+ break;
+ case QRhiShaderResourceBinding::SampledTexture:
+ {
+ QVkTexture *texD = QRHI_RES(QVkTexture, b->u.stex.tex);
+ QVkSampler *samplerD = QRHI_RES(QVkSampler, b->u.stex.sampler);
+ writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+ bd.stex.texId = texD->m_id;
+ bd.stex.texGeneration = texD->generation;
+ bd.stex.samplerId = samplerD->m_id;
+ bd.stex.samplerGeneration = samplerD->generation;
+ VkDescriptorImageInfo imageInfo;
+ imageInfo.sampler = samplerD->sampler;
+ imageInfo.imageView = texD->imageView;
+ imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+ imageInfos.append(imageInfo);
+ writeInfo.pImageInfo = &imageInfos.last();
+ }
+ break;
+ default:
+ continue;
+ }
+
+ writeInfos.append(writeInfo);
+ }
+ ++frameSlot;
+ }
+
+ df->vkUpdateDescriptorSets(dev, writeInfos.count(), writeInfos.constData(), 0, nullptr);
+}
+
+static inline bool accessIsWrite(VkAccessFlags access)
+{
+ return (access & VK_ACCESS_SHADER_WRITE_BIT) != 0
+ || (access & VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT) != 0
+ || (access & VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT) != 0
+ || (access & VK_ACCESS_TRANSFER_WRITE_BIT) != 0
+ || (access & VK_ACCESS_HOST_WRITE_BIT) != 0
+ || (access & VK_ACCESS_MEMORY_WRITE_BIT) != 0;
+}
+
+void QRhiVulkan::trackedBufferBarrier(QVkCommandBuffer *cbD, QVkBuffer *bufD, int slot,
+ VkAccessFlags access, VkPipelineStageFlags stage)
+{
+ Q_ASSERT(!inPass);
+ Q_ASSERT(access && stage);
+ QVkBuffer::UsageState &s(bufD->usageState[slot]);
+ if (!s.stage) {
+ s.access = access;
+ s.stage = stage;
+ return;
+ }
+
+ if (s.access == access && s.stage == stage) {
+ // No need to flood with unnecessary read-after-read barriers.
+ // Write-after-write is a different matter, however.
+ if (!accessIsWrite(access))
+ return;
+ }
+
+ VkBufferMemoryBarrier bufMemBarrier;
+ memset(&bufMemBarrier, 0, sizeof(bufMemBarrier));
+ bufMemBarrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
+ bufMemBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+ bufMemBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+ bufMemBarrier.srcAccessMask = s.access;
+ bufMemBarrier.dstAccessMask = access;
+ bufMemBarrier.buffer = bufD->buffers[slot];
+ bufMemBarrier.size = VK_WHOLE_SIZE;
+
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::BufferBarrier;
+ cmd.args.bufferBarrier.srcStageMask = s.stage;
+ cmd.args.bufferBarrier.dstStageMask = stage;
+ cmd.args.bufferBarrier.desc = bufMemBarrier;
+ cbD->commands.append(cmd);
+
+ s.access = access;
+ s.stage = stage;
+}
+
+void QRhiVulkan::trackedImageBarrier(QVkCommandBuffer *cbD, QVkTexture *texD,
+ VkImageLayout layout, VkAccessFlags access, VkPipelineStageFlags stage)
+{
+ Q_ASSERT(!inPass);
+ Q_ASSERT(layout && access && stage);
+ QVkTexture::UsageState &s(texD->usageState);
+ if (s.access == access && s.stage == stage && s.layout == layout) {
+ if (!accessIsWrite(access))
+ return;
+ }
+
+ VkImageMemoryBarrier barrier;
+ memset(&barrier, 0, sizeof(barrier));
+ barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
+ barrier.subresourceRange.aspectMask = !isDepthTextureFormat(texD->m_format)
+ ? VK_IMAGE_ASPECT_COLOR_BIT : VK_IMAGE_ASPECT_DEPTH_BIT;
+ barrier.subresourceRange.baseMipLevel = 0;
+ barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS;
+ barrier.subresourceRange.baseArrayLayer = 0;
+ barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS;
+ barrier.oldLayout = s.layout; // new textures have this set to PREINITIALIZED
+ barrier.newLayout = layout;
+ barrier.srcAccessMask = s.access; // may be 0 but that's fine
+ barrier.dstAccessMask = access;
+ barrier.image = texD->image;
+
+ VkPipelineStageFlags srcStage = s.stage;
+ // stage mask cannot be 0
+ if (!srcStage)
+ srcStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
+
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::ImageBarrier;
+ cmd.args.imageBarrier.srcStageMask = srcStage;
+ cmd.args.imageBarrier.dstStageMask = stage;
+ cmd.args.imageBarrier.desc = barrier;
+ cbD->commands.append(cmd);
+
+ s.layout = layout;
+ s.access = access;
+ s.stage = stage;
+}
+
+void QRhiVulkan::subresourceBarrier(QVkCommandBuffer *cbD, VkImage image,
+ VkImageLayout oldLayout, VkImageLayout newLayout,
+ VkAccessFlags srcAccess, VkAccessFlags dstAccess,
+ VkPipelineStageFlags srcStage, VkPipelineStageFlags dstStage,
+ int startLayer, int layerCount,
+ int startLevel, int levelCount)
+{
+ Q_ASSERT(!inPass);
+ VkImageMemoryBarrier barrier;
+ memset(&barrier, 0, sizeof(barrier));
+ barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
+ barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ barrier.subresourceRange.baseMipLevel = startLevel;
+ barrier.subresourceRange.levelCount = levelCount;
+ barrier.subresourceRange.baseArrayLayer = startLayer;
+ barrier.subresourceRange.layerCount = layerCount;
+ barrier.oldLayout = oldLayout;
+ barrier.newLayout = newLayout;
+ barrier.srcAccessMask = srcAccess;
+ barrier.dstAccessMask = dstAccess;
+ barrier.image = image;
+
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::ImageBarrier;
+ cmd.args.imageBarrier.srcStageMask = srcStage;
+ cmd.args.imageBarrier.dstStageMask = dstStage;
+ cmd.args.imageBarrier.desc = barrier;
+ cbD->commands.append(cmd);
+}
+
+VkDeviceSize QRhiVulkan::subresUploadByteSize(const QRhiTextureSubresourceUploadDescription &subresDesc) const
+{
+ VkDeviceSize size = 0;
+ const qsizetype imageSizeBytes = subresDesc.image().isNull() ?
+ subresDesc.data().size() : subresDesc.image().sizeInBytes();
+ if (imageSizeBytes > 0)
+ size += aligned(imageSizeBytes, texbufAlign);
+ return size;
+}
+
+void QRhiVulkan::prepareUploadSubres(QVkTexture *texD, int layer, int level,
+ const QRhiTextureSubresourceUploadDescription &subresDesc,
+ size_t *curOfs, void *mp,
+ BufferImageCopyList *copyInfos)
+{
+ qsizetype copySizeBytes = 0;
+ qsizetype imageSizeBytes = 0;
+ const void *src = nullptr;
+
+ VkBufferImageCopy copyInfo;
+ memset(&copyInfo, 0, sizeof(copyInfo));
+ copyInfo.bufferOffset = *curOfs;
+ copyInfo.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ copyInfo.imageSubresource.mipLevel = level;
+ copyInfo.imageSubresource.baseArrayLayer = layer;
+ copyInfo.imageSubresource.layerCount = 1;
+ copyInfo.imageExtent.depth = 1;
+
+ const QByteArray rawData = subresDesc.data();
+ const QPoint dp = subresDesc.destinationTopLeft();
+ QImage image = subresDesc.image();
+ if (!image.isNull()) {
+ copySizeBytes = imageSizeBytes = image.sizeInBytes();
+ QSize size = image.size();
+ src = image.constBits();
+ // Scanlines in QImage are 4 byte aligned so bpl must
+ // be taken into account for bufferRowLength.
+ int bpc = qMax(1, image.depth() / 8);
+ // this is in pixels, not bytes, to make it more complicated...
+ copyInfo.bufferRowLength = image.bytesPerLine() / bpc;
+ if (!subresDesc.sourceSize().isEmpty() || !subresDesc.sourceTopLeft().isNull()) {
+ const int sx = subresDesc.sourceTopLeft().x();
+ const int sy = subresDesc.sourceTopLeft().y();
+ if (!subresDesc.sourceSize().isEmpty())
+ size = subresDesc.sourceSize();
+ if (image.depth() == 32) {
+ // The staging buffer will get the full image
+ // regardless, just adjust the vk
+ // buffer-to-image copy start offset.
+ copyInfo.bufferOffset += sy * image.bytesPerLine() + sx * 4;
+ // bufferRowLength remains set to the original image's width
+ } else {
+ image = image.copy(sx, sy, size.width(), size.height());
+ src = image.constBits();
+ // The staging buffer gets the slice only. The rest of the
+ // space reserved for this mip will be unused.
+ copySizeBytes = image.sizeInBytes();
+ bpc = qMax(1, image.depth() / 8);
+ copyInfo.bufferRowLength = image.bytesPerLine() / bpc;
+ }
+ }
+ copyInfo.imageOffset.x = dp.x();
+ copyInfo.imageOffset.y = dp.y();
+ copyInfo.imageExtent.width = size.width();
+ copyInfo.imageExtent.height = size.height();
+ copyInfos->append(copyInfo);
+ } else if (!rawData.isEmpty() && isCompressedFormat(texD->m_format)) {
+ copySizeBytes = imageSizeBytes = rawData.size();
+ src = rawData.constData();
+ QSize size = q->sizeForMipLevel(level, texD->m_pixelSize);
+ const int subresw = size.width();
+ const int subresh = size.height();
+ if (!subresDesc.sourceSize().isEmpty())
+ size = subresDesc.sourceSize();
+ const int w = size.width();
+ const int h = size.height();
+ QSize blockDim;
+ compressedFormatInfo(texD->m_format, QSize(w, h), nullptr, nullptr, &blockDim);
+ // x and y must be multiples of the block width and height
+ copyInfo.imageOffset.x = aligned(dp.x(), blockDim.width());
+ copyInfo.imageOffset.y = aligned(dp.y(), blockDim.height());
+ // width and height must be multiples of the block width and height
+ // or x + width and y + height must equal the subresource width and height
+ copyInfo.imageExtent.width = dp.x() + w == subresw ? w : aligned(w, blockDim.width());
+ copyInfo.imageExtent.height = dp.y() + h == subresh ? h : aligned(h, blockDim.height());
+ copyInfos->append(copyInfo);
+ } else if (!rawData.isEmpty()) {
+ copySizeBytes = imageSizeBytes = rawData.size();
+ src = rawData.constData();
+ QSize size = q->sizeForMipLevel(level, texD->m_pixelSize);
+ if (!subresDesc.sourceSize().isEmpty())
+ size = subresDesc.sourceSize();
+ copyInfo.imageOffset.x = dp.x();
+ copyInfo.imageOffset.y = dp.y();
+ copyInfo.imageExtent.width = size.width();
+ copyInfo.imageExtent.height = size.height();
+ copyInfos->append(copyInfo);
+ } else {
+ qWarning("Invalid texture upload for %p layer=%d mip=%d", texD, layer, level);
+ }
+
+ memcpy(reinterpret_cast<char *>(mp) + *curOfs, src, copySizeBytes);
+ *curOfs += aligned(imageSizeBytes, texbufAlign);
+}
+
+void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ QRhiResourceUpdateBatchPrivate *ud = QRhiResourceUpdateBatchPrivate::get(resourceUpdates);
+ QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
+
+ for (const QRhiResourceUpdateBatchPrivate::DynamicBufferUpdate &u : ud->dynamicBufferUpdates) {
+ QVkBuffer *bufD = QRHI_RES(QVkBuffer, u.buf);
+ Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic);
+ for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i)
+ bufD->pendingDynamicUpdates[i].append(u);
+ }
+
+ for (const QRhiResourceUpdateBatchPrivate::StaticBufferUpload &u : ud->staticBufferUploads) {
+ QVkBuffer *bufD = QRHI_RES(QVkBuffer, u.buf);
+ Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic);
+ Q_ASSERT(u.offset + u.data.size() <= bufD->m_size);
+
+ if (!bufD->stagingBuffers[currentFrameSlot]) {
+ VkBufferCreateInfo bufferInfo;
+ memset(&bufferInfo, 0, sizeof(bufferInfo));
+ bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
+ // must cover the entire buffer - this way multiple, partial updates per frame
+ // are supported even when the staging buffer is reused (Static)
+ bufferInfo.size = bufD->m_size;
+ bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
+
+ VmaAllocationCreateInfo allocInfo;
+ memset(&allocInfo, 0, sizeof(allocInfo));
+ allocInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
+
+ VmaAllocation allocation;
+ VkResult err = vmaCreateBuffer(toVmaAllocator(allocator), &bufferInfo, &allocInfo,
+ &bufD->stagingBuffers[currentFrameSlot], &allocation, nullptr);
+ if (err == VK_SUCCESS) {
+ bufD->stagingAllocations[currentFrameSlot] = allocation;
+ QRHI_PROF_F(newBufferStagingArea(bufD, currentFrameSlot, bufD->m_size));
+ } else {
+ qWarning("Failed to create staging buffer of size %d: %d", bufD->m_size, err);
+ continue;
+ }
+ }
+
+ void *p = nullptr;
+ VmaAllocation a = toVmaAllocation(bufD->stagingAllocations[currentFrameSlot]);
+ VkResult err = vmaMapMemory(toVmaAllocator(allocator), a, &p);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to map buffer: %d", err);
+ continue;
+ }
+ memcpy(static_cast<uchar *>(p) + u.offset, u.data.constData(), u.data.size());
+ vmaUnmapMemory(toVmaAllocator(allocator), a);
+ vmaFlushAllocation(toVmaAllocator(allocator), a, u.offset, u.data.size());
+
+ trackedBufferBarrier(cbD, bufD, 0,
+ VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
+
+ VkBufferCopy copyInfo;
+ memset(&copyInfo, 0, sizeof(copyInfo));
+ copyInfo.srcOffset = u.offset;
+ copyInfo.dstOffset = u.offset;
+ copyInfo.size = u.data.size();
+
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::CopyBuffer;
+ cmd.args.copyBuffer.src = bufD->stagingBuffers[currentFrameSlot];
+ cmd.args.copyBuffer.dst = bufD->buffers[0];
+ cmd.args.copyBuffer.desc = copyInfo;
+ cbD->commands.append(cmd);
+
+ // Where's the barrier for read-after-write? (assuming the common case
+ // of binding this buffer as vertex/index, or, less likely, as uniform
+ // buffer, in a renderpass later on) That is handled by the pass
+ // resource tracking: the appropriate pipeline barrier will be
+ // generated and recorded right before the renderpass, that binds this
+ // buffer in one of its commands, gets its BeginRenderPass recorded.
+
+ bufD->lastActiveFrameSlot = currentFrameSlot;
+
+ if (bufD->m_type == QRhiBuffer::Immutable) {
+ QRhiVulkan::DeferredReleaseEntry e;
+ e.type = QRhiVulkan::DeferredReleaseEntry::StagingBuffer;
+ e.lastActiveFrameSlot = currentFrameSlot;
+ e.stagingBuffer.stagingBuffer = bufD->stagingBuffers[currentFrameSlot];
+ e.stagingBuffer.stagingAllocation = bufD->stagingAllocations[currentFrameSlot];
+ bufD->stagingBuffers[currentFrameSlot] = VK_NULL_HANDLE;
+ bufD->stagingAllocations[currentFrameSlot] = nullptr;
+ releaseQueue.append(e);
+ QRHI_PROF_F(releaseBufferStagingArea(bufD, currentFrameSlot));
+ }
+ }
+
+ for (const QRhiResourceUpdateBatchPrivate::TextureOp &u : ud->textureOps) {
+ if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Upload) {
+ QVkTexture *utexD = QRHI_RES(QVkTexture, u.upload.tex);
+ // batch into a single staging buffer and a single CopyBufferToImage with multiple copyInfos
+ VkDeviceSize stagingSize = 0;
+ for (int layer = 0; layer < QRhi::MAX_LAYERS; ++layer) {
+ for (int level = 0; level < QRhi::MAX_LEVELS; ++level) {
+ for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.upload.subresDesc[layer][level]))
+ stagingSize += subresUploadByteSize(subresDesc);
+ }
+ }
+
+ Q_ASSERT(!utexD->stagingBuffers[currentFrameSlot]);
+ VkBufferCreateInfo bufferInfo;
+ memset(&bufferInfo, 0, sizeof(bufferInfo));
+ bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
+ bufferInfo.size = stagingSize;
+ bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
+
+ VmaAllocationCreateInfo allocInfo;
+ memset(&allocInfo, 0, sizeof(allocInfo));
+ allocInfo.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;
+
+ VmaAllocation allocation;
+ VkResult err = vmaCreateBuffer(toVmaAllocator(allocator), &bufferInfo, &allocInfo,
+ &utexD->stagingBuffers[currentFrameSlot], &allocation, nullptr);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create image staging buffer of size %d: %d", int(stagingSize), err);
+ continue;
+ }
+ utexD->stagingAllocations[currentFrameSlot] = allocation;
+ QRHI_PROF_F(newTextureStagingArea(utexD, currentFrameSlot, stagingSize));
+
+ BufferImageCopyList copyInfos;
+ size_t curOfs = 0;
+ void *mp = nullptr;
+ VmaAllocation a = toVmaAllocation(utexD->stagingAllocations[currentFrameSlot]);
+ err = vmaMapMemory(toVmaAllocator(allocator), a, &mp);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to map image data: %d", err);
+ continue;
+ }
+
+ for (int layer = 0; layer < QRhi::MAX_LAYERS; ++layer) {
+ for (int level = 0; level < QRhi::MAX_LEVELS; ++level) {
+ const QVector<QRhiTextureSubresourceUploadDescription> &srd(u.upload.subresDesc[layer][level]);
+ if (srd.isEmpty())
+ continue;
+ for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(srd)) {
+ prepareUploadSubres(utexD, layer, level,
+ subresDesc, &curOfs, mp, &copyInfos);
+ }
+ }
+ }
+ vmaUnmapMemory(toVmaAllocator(allocator), a);
+ vmaFlushAllocation(toVmaAllocator(allocator), a, 0, stagingSize);
+
+ trackedImageBarrier(cbD, utexD, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
+
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::CopyBufferToImage;
+ cmd.args.copyBufferToImage.src = utexD->stagingBuffers[currentFrameSlot];
+ cmd.args.copyBufferToImage.dst = utexD->image;
+ cmd.args.copyBufferToImage.dstLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
+ cmd.args.copyBufferToImage.count = copyInfos.count();
+ cmd.args.copyBufferToImage.bufferImageCopyIndex = cbD->pools.bufferImageCopy.count();
+ cbD->pools.bufferImageCopy.append(copyInfos.constData(), copyInfos.count());
+ cbD->commands.append(cmd);
+
+ // no reuse of staging, this is intentional
+ QRhiVulkan::DeferredReleaseEntry e;
+ e.type = QRhiVulkan::DeferredReleaseEntry::StagingBuffer;
+ e.lastActiveFrameSlot = currentFrameSlot;
+ e.stagingBuffer.stagingBuffer = utexD->stagingBuffers[currentFrameSlot];
+ e.stagingBuffer.stagingAllocation = utexD->stagingAllocations[currentFrameSlot];
+ utexD->stagingBuffers[currentFrameSlot] = VK_NULL_HANDLE;
+ utexD->stagingAllocations[currentFrameSlot] = nullptr;
+ releaseQueue.append(e);
+ QRHI_PROF_F(releaseTextureStagingArea(utexD, currentFrameSlot));
+
+ // Similarly to buffers, transitioning away from DST is done later,
+ // when a renderpass using the texture is encountered.
+
+ utexD->lastActiveFrameSlot = currentFrameSlot;
+ } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Copy) {
+ Q_ASSERT(u.copy.src && u.copy.dst);
+ if (u.copy.src == u.copy.dst) {
+ qWarning("Texture copy with matching source and destination is not supported");
+ continue;
+ }
+ QVkTexture *srcD = QRHI_RES(QVkTexture, u.copy.src);
+ QVkTexture *dstD = QRHI_RES(QVkTexture, u.copy.dst);
+
+ VkImageCopy region;
+ memset(&region, 0, sizeof(region));
+
+ region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ region.srcSubresource.mipLevel = u.copy.desc.sourceLevel();
+ region.srcSubresource.baseArrayLayer = u.copy.desc.sourceLayer();
+ region.srcSubresource.layerCount = 1;
+
+ region.srcOffset.x = u.copy.desc.sourceTopLeft().x();
+ region.srcOffset.y = u.copy.desc.sourceTopLeft().y();
+
+ region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ region.dstSubresource.mipLevel = u.copy.desc.destinationLevel();
+ region.dstSubresource.baseArrayLayer = u.copy.desc.destinationLayer();
+ region.dstSubresource.layerCount = 1;
+
+ region.dstOffset.x = u.copy.desc.destinationTopLeft().x();
+ region.dstOffset.y = u.copy.desc.destinationTopLeft().y();
+
+ const QSize size = u.copy.desc.pixelSize().isEmpty() ? srcD->m_pixelSize : u.copy.desc.pixelSize();
+ region.extent.width = size.width();
+ region.extent.height = size.height();
+ region.extent.depth = 1;
+
+ trackedImageBarrier(cbD, srcD, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ VK_ACCESS_TRANSFER_READ_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
+ trackedImageBarrier(cbD, dstD, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
+
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::CopyImage;
+ cmd.args.copyImage.src = srcD->image;
+ cmd.args.copyImage.srcLayout = srcD->usageState.layout;
+ cmd.args.copyImage.dst = dstD->image;
+ cmd.args.copyImage.dstLayout = dstD->usageState.layout;
+ cmd.args.copyImage.desc = region;
+ cbD->commands.append(cmd);
+
+ srcD->lastActiveFrameSlot = dstD->lastActiveFrameSlot = currentFrameSlot;
+ } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Read) {
+ ActiveReadback aRb;
+ aRb.activeFrameSlot = currentFrameSlot;
+ aRb.desc = u.read.rb;
+ aRb.result = u.read.result;
+
+ QVkTexture *texD = QRHI_RES(QVkTexture, u.read.rb.texture());
+ QVkSwapChain *swapChainD = nullptr;
+ if (texD) {
+ if (texD->samples > VK_SAMPLE_COUNT_1_BIT) {
+ qWarning("Multisample texture cannot be read back");
+ continue;
+ }
+ aRb.pixelSize = u.read.rb.level() > 0 ? q->sizeForMipLevel(u.read.rb.level(), texD->m_pixelSize)
+ : texD->m_pixelSize;
+ aRb.format = texD->m_format;
+ texD->lastActiveFrameSlot = currentFrameSlot;
+ } else {
+ Q_ASSERT(currentSwapChain);
+ swapChainD = QRHI_RES(QVkSwapChain, currentSwapChain);
+ if (!swapChainD->supportsReadback) {
+ qWarning("Swapchain does not support readback");
+ continue;
+ }
+ aRb.pixelSize = swapChainD->pixelSize;
+ aRb.format = colorTextureFormatFromVkFormat(swapChainD->colorFormat, nullptr);
+ if (aRb.format == QRhiTexture::UnknownFormat)
+ continue;
+
+ // Multisample swapchains need nothing special since resolving
+ // happens when ending a renderpass.
+ }
+ textureFormatInfo(aRb.format, aRb.pixelSize, nullptr, &aRb.bufSize);
+
+ // Create a host visible buffer.
+ VkBufferCreateInfo bufferInfo;
+ memset(&bufferInfo, 0, sizeof(bufferInfo));
+ bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
+ bufferInfo.size = aRb.bufSize;
+ bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
+
+ VmaAllocationCreateInfo allocInfo;
+ memset(&allocInfo, 0, sizeof(allocInfo));
+ allocInfo.usage = VMA_MEMORY_USAGE_GPU_TO_CPU;
+
+ VmaAllocation allocation;
+ VkResult err = vmaCreateBuffer(toVmaAllocator(allocator), &bufferInfo, &allocInfo, &aRb.buf, &allocation, nullptr);
+ if (err == VK_SUCCESS) {
+ aRb.bufAlloc = allocation;
+ QRHI_PROF_F(newReadbackBuffer(quint64(aRb.buf),
+ texD ? static_cast<QRhiResource *>(texD) : static_cast<QRhiResource *>(swapChainD),
+ aRb.bufSize));
+ } else {
+ qWarning("Failed to create readback buffer of size %u: %d", aRb.bufSize, err);
+ continue;
+ }
+
+ // Copy from the (optimal and not host visible) image into the buffer.
+ VkBufferImageCopy copyDesc;
+ memset(&copyDesc, 0, sizeof(copyDesc));
+ copyDesc.bufferOffset = 0;
+ copyDesc.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ copyDesc.imageSubresource.mipLevel = u.read.rb.level();
+ copyDesc.imageSubresource.baseArrayLayer = u.read.rb.layer();
+ copyDesc.imageSubresource.layerCount = 1;
+ copyDesc.imageExtent.width = aRb.pixelSize.width();
+ copyDesc.imageExtent.height = aRb.pixelSize.height();
+ copyDesc.imageExtent.depth = 1;
+
+ if (texD) {
+ trackedImageBarrier(cbD, texD, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ VK_ACCESS_TRANSFER_READ_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::CopyImageToBuffer;
+ cmd.args.copyImageToBuffer.src = texD->image;
+ cmd.args.copyImageToBuffer.srcLayout = texD->usageState.layout;
+ cmd.args.copyImageToBuffer.dst = aRb.buf;
+ cmd.args.copyImageToBuffer.desc = copyDesc;
+ cbD->commands.append(cmd);
+ } else {
+ // use the swapchain image
+ VkImage image = swapChainD->imageRes[swapChainD->currentImageIndex].image;
+ if (!swapChainD->imageRes[swapChainD->currentImageIndex].transferSource) {
+ subresourceBarrier(cbD, image,
+ VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ VK_ACCESS_MEMORY_READ_BIT, VK_ACCESS_TRANSFER_READ_BIT,
+ VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
+ 0, 1,
+ 0, 1);
+ swapChainD->imageRes[swapChainD->currentImageIndex].transferSource = true;
+ }
+
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::CopyImageToBuffer;
+ cmd.args.copyImageToBuffer.src = image;
+ cmd.args.copyImageToBuffer.srcLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
+ cmd.args.copyImageToBuffer.dst = aRb.buf;
+ cmd.args.copyImageToBuffer.desc = copyDesc;
+ cbD->commands.append(cmd);
+ }
+
+ activeReadbacks.append(aRb);
+ } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::MipGen) {
+ QVkTexture *utexD = QRHI_RES(QVkTexture, u.mipgen.tex);
+ Q_ASSERT(utexD->m_flags.testFlag(QRhiTexture::UsedWithGenerateMips));
+ int w = utexD->m_pixelSize.width();
+ int h = utexD->m_pixelSize.height();
+
+ VkImageLayout origLayout = utexD->usageState.layout;
+ VkAccessFlags origAccess = utexD->usageState.access;
+ VkPipelineStageFlags origStage = utexD->usageState.stage;
+ if (!origStage)
+ origStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
+
+ for (uint level = 1; level < utexD->mipLevelCount; ++level) {
+ if (level == 1) {
+ subresourceBarrier(cbD, utexD->image,
+ origLayout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ origAccess, VK_ACCESS_TRANSFER_READ_BIT,
+ origStage, VK_PIPELINE_STAGE_TRANSFER_BIT,
+ u.mipgen.layer, 1,
+ level - 1, 1);
+ } else {
+ subresourceBarrier(cbD, utexD->image,
+ VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT,
+ VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
+ u.mipgen.layer, 1,
+ level - 1, 1);
+ }
+
+ subresourceBarrier(cbD, utexD->image,
+ origLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ origAccess, VK_ACCESS_TRANSFER_WRITE_BIT,
+ origStage, VK_PIPELINE_STAGE_TRANSFER_BIT,
+ u.mipgen.layer, 1,
+ level, 1);
+
+ VkImageBlit region;
+ memset(&region, 0, sizeof(region));
+
+ region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ region.srcSubresource.mipLevel = level - 1;
+ region.srcSubresource.baseArrayLayer = u.mipgen.layer;
+ region.srcSubresource.layerCount = 1;
+
+ region.srcOffsets[1].x = qMax(1, w);
+ region.srcOffsets[1].y = qMax(1, h);
+ region.srcOffsets[1].z = 1;
+
+ region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ region.dstSubresource.mipLevel = level;
+ region.dstSubresource.baseArrayLayer = u.mipgen.layer;
+ region.dstSubresource.layerCount = 1;
+
+ region.dstOffsets[1].x = qMax(1, w >> 1);
+ region.dstOffsets[1].y = qMax(1, h >> 1);
+ region.dstOffsets[1].z = 1;
+
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::BlitImage;
+ cmd.args.blitImage.src = utexD->image;
+ cmd.args.blitImage.srcLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
+ cmd.args.blitImage.dst = utexD->image;
+ cmd.args.blitImage.dstLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
+ cmd.args.blitImage.filter = VK_FILTER_LINEAR;
+ cmd.args.blitImage.desc = region;
+ cbD->commands.append(cmd);
+
+ w >>= 1;
+ h >>= 1;
+ }
+
+ if (utexD->mipLevelCount > 1) {
+ subresourceBarrier(cbD, utexD->image,
+ VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, origLayout,
+ VK_ACCESS_TRANSFER_READ_BIT, origAccess,
+ VK_PIPELINE_STAGE_TRANSFER_BIT, origStage,
+ u.mipgen.layer, 1,
+ 0, utexD->mipLevelCount - 1);
+ subresourceBarrier(cbD, utexD->image,
+ VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, origLayout,
+ VK_ACCESS_TRANSFER_WRITE_BIT, origAccess,
+ VK_PIPELINE_STAGE_TRANSFER_BIT, origStage,
+ u.mipgen.layer, 1,
+ utexD->mipLevelCount - 1, 1);
+ }
+
+ utexD->lastActiveFrameSlot = currentFrameSlot;
+ }
+ }
+
+ ud->free();
+}
+
+void QRhiVulkan::executeBufferHostWritesForCurrentFrame(QVkBuffer *bufD)
+{
+ QVector<QRhiResourceUpdateBatchPrivate::DynamicBufferUpdate> &updates(bufD->pendingDynamicUpdates[currentFrameSlot]);
+ if (updates.isEmpty())
+ return;
+
+ Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic);
+ void *p = nullptr;
+ VmaAllocation a = toVmaAllocation(bufD->allocations[currentFrameSlot]);
+ // The vmaMap/Unmap are basically a no-op when persistently mapped since it
+ // refcounts; this is great because we don't need to care if the allocation
+ // was created as persistently mapped or not.
+ VkResult err = vmaMapMemory(toVmaAllocator(allocator), a, &p);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to map buffer: %d", err);
+ return;
+ }
+ int changeBegin = -1;
+ int changeEnd = -1;
+ for (const QRhiResourceUpdateBatchPrivate::DynamicBufferUpdate &u : updates) {
+ Q_ASSERT(bufD == QRHI_RES(QVkBuffer, u.buf));
+ memcpy(static_cast<char *>(p) + u.offset, u.data.constData(), u.data.size());
+ if (changeBegin == -1 || u.offset < changeBegin)
+ changeBegin = u.offset;
+ if (changeEnd == -1 || u.offset + u.data.size() > changeEnd)
+ changeEnd = u.offset + u.data.size();
+ }
+ vmaUnmapMemory(toVmaAllocator(allocator), a);
+ if (changeBegin >= 0)
+ vmaFlushAllocation(toVmaAllocator(allocator), a, changeBegin, changeEnd - changeBegin);
+
+ updates.clear();
+}
+
+static void qrhivk_releaseBuffer(const QRhiVulkan::DeferredReleaseEntry &e, void *allocator)
+{
+ for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
+ vmaDestroyBuffer(toVmaAllocator(allocator), e.buffer.buffers[i], toVmaAllocation(e.buffer.allocations[i]));
+ vmaDestroyBuffer(toVmaAllocator(allocator), e.buffer.stagingBuffers[i], toVmaAllocation(e.buffer.stagingAllocations[i]));
+ }
+}
+
+static void qrhivk_releaseRenderBuffer(const QRhiVulkan::DeferredReleaseEntry &e, VkDevice dev, QVulkanDeviceFunctions *df)
+{
+ df->vkDestroyImageView(dev, e.renderBuffer.imageView, nullptr);
+ df->vkDestroyImage(dev, e.renderBuffer.image, nullptr);
+ df->vkFreeMemory(dev, e.renderBuffer.memory, nullptr);
+}
+
+static void qrhivk_releaseTexture(const QRhiVulkan::DeferredReleaseEntry &e, VkDevice dev, QVulkanDeviceFunctions *df, void *allocator)
+{
+ df->vkDestroyImageView(dev, e.texture.imageView, nullptr);
+ vmaDestroyImage(toVmaAllocator(allocator), e.texture.image, toVmaAllocation(e.texture.allocation));
+ for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i)
+ vmaDestroyBuffer(toVmaAllocator(allocator), e.texture.stagingBuffers[i], toVmaAllocation(e.texture.stagingAllocations[i]));
+}
+
+static void qrhivk_releaseSampler(const QRhiVulkan::DeferredReleaseEntry &e, VkDevice dev, QVulkanDeviceFunctions *df)
+{
+ df->vkDestroySampler(dev, e.sampler.sampler, nullptr);
+}
+
+void QRhiVulkan::executeDeferredReleases(bool forced)
+{
+ for (int i = releaseQueue.count() - 1; i >= 0; --i) {
+ const QRhiVulkan::DeferredReleaseEntry &e(releaseQueue[i]);
+ if (forced || currentFrameSlot == e.lastActiveFrameSlot || e.lastActiveFrameSlot < 0) {
+ switch (e.type) {
+ case QRhiVulkan::DeferredReleaseEntry::Pipeline:
+ df->vkDestroyPipeline(dev, e.pipelineState.pipeline, nullptr);
+ df->vkDestroyPipelineLayout(dev, e.pipelineState.layout, nullptr);
+ break;
+ case QRhiVulkan::DeferredReleaseEntry::ShaderResourceBindings:
+ df->vkDestroyDescriptorSetLayout(dev, e.shaderResourceBindings.layout, nullptr);
+ if (e.shaderResourceBindings.poolIndex >= 0) {
+ descriptorPools[e.shaderResourceBindings.poolIndex].refCount -= 1;
+ Q_ASSERT(descriptorPools[e.shaderResourceBindings.poolIndex].refCount >= 0);
+ }
+ break;
+ case QRhiVulkan::DeferredReleaseEntry::Buffer:
+ qrhivk_releaseBuffer(e, allocator);
+ break;
+ case QRhiVulkan::DeferredReleaseEntry::RenderBuffer:
+ qrhivk_releaseRenderBuffer(e, dev, df);
+ break;
+ case QRhiVulkan::DeferredReleaseEntry::Texture:
+ qrhivk_releaseTexture(e, dev, df, allocator);
+ break;
+ case QRhiVulkan::DeferredReleaseEntry::Sampler:
+ qrhivk_releaseSampler(e, dev, df);
+ break;
+ case QRhiVulkan::DeferredReleaseEntry::TextureRenderTarget:
+ df->vkDestroyFramebuffer(dev, e.textureRenderTarget.fb, nullptr);
+ for (int att = 0; att < QVkRenderTargetData::MAX_COLOR_ATTACHMENTS; ++att) {
+ df->vkDestroyImageView(dev, e.textureRenderTarget.rtv[att], nullptr);
+ df->vkDestroyImageView(dev, e.textureRenderTarget.resrtv[att], nullptr);
+ }
+ break;
+ case QRhiVulkan::DeferredReleaseEntry::RenderPass:
+ df->vkDestroyRenderPass(dev, e.renderPass.rp, nullptr);
+ break;
+ case QRhiVulkan::DeferredReleaseEntry::StagingBuffer:
+ vmaDestroyBuffer(toVmaAllocator(allocator), e.stagingBuffer.stagingBuffer, toVmaAllocation(e.stagingBuffer.stagingAllocation));
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ releaseQueue.removeAt(i);
+ }
+ }
+}
+
+void QRhiVulkan::finishActiveReadbacks(bool forced)
+{
+ QVarLengthArray<std::function<void()>, 4> completedCallbacks;
+ QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
+
+ for (int i = activeReadbacks.count() - 1; i >= 0; --i) {
+ const QRhiVulkan::ActiveReadback &aRb(activeReadbacks[i]);
+ if (forced || currentFrameSlot == aRb.activeFrameSlot || aRb.activeFrameSlot < 0) {
+ aRb.result->format = aRb.format;
+ aRb.result->pixelSize = aRb.pixelSize;
+ aRb.result->data.resize(aRb.bufSize);
+ void *p = nullptr;
+ VmaAllocation a = toVmaAllocation(aRb.bufAlloc);
+ VkResult err = vmaMapMemory(toVmaAllocator(allocator), a, &p);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to map readback buffer: %d", err);
+ continue;
+ }
+ memcpy(aRb.result->data.data(), p, aRb.bufSize);
+ vmaUnmapMemory(toVmaAllocator(allocator), a);
+
+ vmaDestroyBuffer(toVmaAllocator(allocator), aRb.buf, a);
+ QRHI_PROF_F(releaseReadbackBuffer(quint64(aRb.buf)));
+
+ if (aRb.result->completed)
+ completedCallbacks.append(aRb.result->completed);
+
+ activeReadbacks.removeAt(i);
+ }
+ }
+
+ for (auto f : completedCallbacks)
+ f();
+}
+
+static struct {
+ VkSampleCountFlagBits mask;
+ int count;
+} qvk_sampleCounts[] = {
+ // keep this sorted by 'count'
+ { VK_SAMPLE_COUNT_1_BIT, 1 },
+ { VK_SAMPLE_COUNT_2_BIT, 2 },
+ { VK_SAMPLE_COUNT_4_BIT, 4 },
+ { VK_SAMPLE_COUNT_8_BIT, 8 },
+ { VK_SAMPLE_COUNT_16_BIT, 16 },
+ { VK_SAMPLE_COUNT_32_BIT, 32 },
+ { VK_SAMPLE_COUNT_64_BIT, 64 }
+};
+
+QVector<int> QRhiVulkan::supportedSampleCounts() const
+{
+ const VkPhysicalDeviceLimits *limits = &physDevProperties.limits;
+ VkSampleCountFlags color = limits->framebufferColorSampleCounts;
+ VkSampleCountFlags depth = limits->framebufferDepthSampleCounts;
+ VkSampleCountFlags stencil = limits->framebufferStencilSampleCounts;
+ QVector<int> result;
+
+ for (size_t i = 0; i < sizeof(qvk_sampleCounts) / sizeof(qvk_sampleCounts[0]); ++i) {
+ if ((color & qvk_sampleCounts[i].mask)
+ && (depth & qvk_sampleCounts[i].mask)
+ && (stencil & qvk_sampleCounts[i].mask))
+ {
+ result.append(qvk_sampleCounts[i].count);
+ }
+ }
+
+ return result;
+}
+
+VkSampleCountFlagBits QRhiVulkan::effectiveSampleCount(int sampleCount)
+{
+ // Stay compatible with QSurfaceFormat and friends where samples == 0 means the same as 1.
+ sampleCount = qBound(1, sampleCount, 64);
+
+ if (!supportedSampleCounts().contains(sampleCount)) {
+ qWarning("Attempted to set unsupported sample count %d", sampleCount);
+ return VK_SAMPLE_COUNT_1_BIT;
+ }
+
+ for (size_t i = 0; i < sizeof(qvk_sampleCounts) / sizeof(qvk_sampleCounts[0]); ++i) {
+ if (qvk_sampleCounts[i].count == sampleCount)
+ return qvk_sampleCounts[i].mask;
+ }
+
+ Q_UNREACHABLE();
+ return VK_SAMPLE_COUNT_1_BIT;
+}
+
+void QRhiVulkan::enqueueTransitionPassResources(QVkCommandBuffer *cbD)
+{
+ cbD->passResTrackers.append(QRhiPassResourceTracker());
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::TransitionPassResources;
+ cmd.args.transitionResources.trackerIndex = cbD->passResTrackers.count() - 1;
+ cbD->commands.append(cmd);
+ cbD->currentPassResTrackerIndex = cbD->passResTrackers.count() - 1;
+}
+
+void QRhiVulkan::recordCommandBuffer(QVkCommandBuffer *cbD)
+{
+ for (QVkCommandBuffer::Command &cmd : cbD->commands) {
+ switch (cmd.cmd) {
+ case QVkCommandBuffer::Command::CopyBuffer:
+ df->vkCmdCopyBuffer(cbD->cb, cmd.args.copyBuffer.src, cmd.args.copyBuffer.dst,
+ 1, &cmd.args.copyBuffer.desc);
+ break;
+ case QVkCommandBuffer::Command::CopyBufferToImage:
+ df->vkCmdCopyBufferToImage(cbD->cb, cmd.args.copyBufferToImage.src, cmd.args.copyBufferToImage.dst,
+ cmd.args.copyBufferToImage.dstLayout,
+ cmd.args.copyBufferToImage.count,
+ cbD->pools.bufferImageCopy.constData() + cmd.args.copyBufferToImage.bufferImageCopyIndex);
+ break;
+ case QVkCommandBuffer::Command::CopyImage:
+ df->vkCmdCopyImage(cbD->cb, cmd.args.copyImage.src, cmd.args.copyImage.srcLayout,
+ cmd.args.copyImage.dst, cmd.args.copyImage.dstLayout,
+ 1, &cmd.args.copyImage.desc);
+ break;
+ case QVkCommandBuffer::Command::CopyImageToBuffer:
+ df->vkCmdCopyImageToBuffer(cbD->cb, cmd.args.copyImageToBuffer.src, cmd.args.copyImageToBuffer.srcLayout,
+ cmd.args.copyImageToBuffer.dst,
+ 1, &cmd.args.copyImageToBuffer.desc);
+ break;
+ case QVkCommandBuffer::Command::ImageBarrier:
+ df->vkCmdPipelineBarrier(cbD->cb, cmd.args.imageBarrier.srcStageMask, cmd.args.imageBarrier.dstStageMask,
+ 0, 0, nullptr, 0, nullptr,
+ 1, &cmd.args.imageBarrier.desc);
+ break;
+ case QVkCommandBuffer::Command::BufferBarrier:
+ df->vkCmdPipelineBarrier(cbD->cb, cmd.args.bufferBarrier.srcStageMask, cmd.args.bufferBarrier.dstStageMask,
+ 0, 0, nullptr,
+ 1, &cmd.args.bufferBarrier.desc,
+ 0, nullptr);
+ break;
+ case QVkCommandBuffer::Command::BlitImage:
+ df->vkCmdBlitImage(cbD->cb, cmd.args.blitImage.src, cmd.args.blitImage.srcLayout,
+ cmd.args.blitImage.dst, cmd.args.blitImage.dstLayout,
+ 1, &cmd.args.blitImage.desc,
+ cmd.args.blitImage.filter);
+ break;
+ case QVkCommandBuffer::Command::BeginRenderPass:
+ cmd.args.beginRenderPass.desc.pClearValues = cbD->pools.clearValue.constData() + cmd.args.beginRenderPass.clearValueIndex;
+ df->vkCmdBeginRenderPass(cbD->cb, &cmd.args.beginRenderPass.desc, VK_SUBPASS_CONTENTS_INLINE);
+ break;
+ case QVkCommandBuffer::Command::EndRenderPass:
+ df->vkCmdEndRenderPass(cbD->cb);
+ break;
+ case QVkCommandBuffer::Command::BindPipeline:
+ df->vkCmdBindPipeline(cbD->cb, cmd.args.bindPipeline.bindPoint, cmd.args.bindPipeline.pipeline);
+ break;
+ case QVkCommandBuffer::Command::BindDescriptorSet:
+ {
+ const uint32_t *offsets = nullptr;
+ if (cmd.args.bindDescriptorSet.dynamicOffsetCount > 0)
+ offsets = cbD->pools.dynamicOffset.constData() + cmd.args.bindDescriptorSet.dynamicOffsetIndex;
+ df->vkCmdBindDescriptorSets(cbD->cb, cmd.args.bindDescriptorSet.bindPoint,
+ cmd.args.bindDescriptorSet.pipelineLayout,
+ 0, 1, &cmd.args.bindDescriptorSet.descSet,
+ cmd.args.bindDescriptorSet.dynamicOffsetCount,
+ offsets);
+ }
+ break;
+ case QVkCommandBuffer::Command::BindVertexBuffer:
+ df->vkCmdBindVertexBuffers(cbD->cb, cmd.args.bindVertexBuffer.startBinding,
+ cmd.args.bindVertexBuffer.count,
+ cbD->pools.vertexBuffer.constData() + cmd.args.bindVertexBuffer.vertexBufferIndex,
+ cbD->pools.vertexBufferOffset.constData() + cmd.args.bindVertexBuffer.vertexBufferOffsetIndex);
+ break;
+ case QVkCommandBuffer::Command::BindIndexBuffer:
+ df->vkCmdBindIndexBuffer(cbD->cb, cmd.args.bindIndexBuffer.buf,
+ cmd.args.bindIndexBuffer.ofs, cmd.args.bindIndexBuffer.type);
+ break;
+ case QVkCommandBuffer::Command::SetViewport:
+ df->vkCmdSetViewport(cbD->cb, 0, 1, &cmd.args.setViewport.viewport);
+ break;
+ case QVkCommandBuffer::Command::SetScissor:
+ df->vkCmdSetScissor(cbD->cb, 0, 1, &cmd.args.setScissor.scissor);
+ break;
+ case QVkCommandBuffer::Command::SetBlendConstants:
+ df->vkCmdSetBlendConstants(cbD->cb, cmd.args.setBlendConstants.c);
+ break;
+ case QVkCommandBuffer::Command::SetStencilRef:
+ df->vkCmdSetStencilReference(cbD->cb, VK_STENCIL_FRONT_AND_BACK, cmd.args.setStencilRef.ref);
+ break;
+ case QVkCommandBuffer::Command::Draw:
+ df->vkCmdDraw(cbD->cb, cmd.args.draw.vertexCount, cmd.args.draw.instanceCount,
+ cmd.args.draw.firstVertex, cmd.args.draw.firstInstance);
+ break;
+ case QVkCommandBuffer::Command::DrawIndexed:
+ df->vkCmdDrawIndexed(cbD->cb, cmd.args.drawIndexed.indexCount, cmd.args.drawIndexed.instanceCount,
+ cmd.args.drawIndexed.firstIndex, cmd.args.drawIndexed.vertexOffset,
+ cmd.args.drawIndexed.firstInstance);
+ break;
+ case QVkCommandBuffer::Command::DebugMarkerBegin:
+ cmd.args.debugMarkerBegin.marker.pMarkerName =
+ cbD->pools.debugMarkerName[cmd.args.debugMarkerBegin.markerNameIndex].constData();
+ vkCmdDebugMarkerBegin(cbD->cb, &cmd.args.debugMarkerBegin.marker);
+ break;
+ case QVkCommandBuffer::Command::DebugMarkerEnd:
+ vkCmdDebugMarkerEnd(cbD->cb);
+ break;
+ case QVkCommandBuffer::Command::DebugMarkerInsert:
+ vkCmdDebugMarkerInsert(cbD->cb, &cmd.args.debugMarkerInsert.marker);
+ break;
+ case QVkCommandBuffer::Command::TransitionPassResources:
+ recordTransitionPassResources(cbD, cbD->passResTrackers[cmd.args.transitionResources.trackerIndex]);
+ break;
+ default:
+ break;
+ }
+ }
+
+ cbD->resetCommands();
+}
+
+static inline VkAccessFlags toVkAccess(QRhiPassResourceTracker::BufferAccess access)
+{
+ switch (access) {
+ case QRhiPassResourceTracker::BufVertexInput:
+ return VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT;
+ case QRhiPassResourceTracker::BufIndexRead:
+ return VK_ACCESS_INDEX_READ_BIT;
+ case QRhiPassResourceTracker::BufUniformRead:
+ return VK_ACCESS_UNIFORM_READ_BIT;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ return 0;
+}
+
+static inline VkPipelineStageFlags toVkPipelineStage(QRhiPassResourceTracker::BufferStage stage)
+{
+ switch (stage) {
+ case QRhiPassResourceTracker::BufVertexInputStage:
+ return VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;
+ case QRhiPassResourceTracker::BufVertexStage:
+ return VK_PIPELINE_STAGE_VERTEX_SHADER_BIT;
+ case QRhiPassResourceTracker::BufFragmentStage:
+ return VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ return 0;
+}
+
+static inline QVkBuffer::UsageState toVkBufferUsageState(QRhiPassResourceTracker::UsageState usage)
+{
+ QVkBuffer::UsageState u;
+ u.access = usage.access;
+ u.stage = usage.stage;
+ return u;
+}
+
+static inline VkImageLayout toVkLayout(QRhiPassResourceTracker::TextureAccess access)
+{
+ switch (access) {
+ case QRhiPassResourceTracker::TexSample:
+ return VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+ case QRhiPassResourceTracker::TexColorOutput:
+ return VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+ case QRhiPassResourceTracker::TexDepthOutput:
+ return VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ return VK_IMAGE_LAYOUT_GENERAL;
+}
+
+static inline VkAccessFlags toVkAccess(QRhiPassResourceTracker::TextureAccess access)
+{
+ switch (access) {
+ case QRhiPassResourceTracker::TexSample:
+ return VK_ACCESS_SHADER_READ_BIT;
+ case QRhiPassResourceTracker::TexColorOutput:
+ return VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+ case QRhiPassResourceTracker::TexDepthOutput:
+ return VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ return 0;
+}
+
+static inline VkPipelineStageFlags toVkPipelineStage(QRhiPassResourceTracker::TextureStage stage)
+{
+ switch (stage) {
+ case QRhiPassResourceTracker::TexVertexStage:
+ return VK_PIPELINE_STAGE_VERTEX_SHADER_BIT;
+ case QRhiPassResourceTracker::TexFragmentStage:
+ return VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
+ case QRhiPassResourceTracker::TexColorOutputStage:
+ return VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+ case QRhiPassResourceTracker::TexDepthOutputStage:
+ return VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ return 0;
+}
+
+static inline QVkTexture::UsageState toVkTextureUsageState(QRhiPassResourceTracker::UsageState usage)
+{
+ QVkTexture::UsageState u;
+ u.layout = VkImageLayout(usage.layout);
+ u.access = usage.access;
+ u.stage = usage.stage;
+ return u;
+}
+
+void QRhiVulkan::trackedRegisterBuffer(QRhiPassResourceTracker *passResTracker,
+ QVkBuffer *bufD,
+ int slot,
+ QRhiPassResourceTracker::BufferAccess access,
+ QRhiPassResourceTracker::BufferStage stage)
+{
+ QVkBuffer::UsageState &u(bufD->usageState[slot]);
+ // The last arg will get ignored if this buffer was already used in the
+ // same pass; that's good because u is not the state at pass start anymore
+ // at that point.
+ passResTracker->registerBufferOnce(bufD, slot, access, stage, toPassTrackerUsageState(u));
+ u.access = toVkAccess(access);
+ u.stage = toVkPipelineStage(stage);
+}
+
+void QRhiVulkan::trackedRegisterTexture(QRhiPassResourceTracker *passResTracker,
+ QVkTexture *texD,
+ QRhiPassResourceTracker::TextureAccess access,
+ QRhiPassResourceTracker::TextureStage stage)
+{
+ QVkTexture::UsageState &u(texD->usageState);
+ // The last arg will get ignored if this buffer was already used in the
+ // same pass; that's good because u is not the state at pass start anymore
+ // at that point.
+ passResTracker->registerTextureOnce(texD, access, stage, toPassTrackerUsageState(u));
+ u.layout = toVkLayout(access);
+ u.access = toVkAccess(access);
+ u.stage = toVkPipelineStage(stage);
+}
+
+void QRhiVulkan::recordTransitionPassResources(QVkCommandBuffer *cbD, const QRhiPassResourceTracker &tracker)
+{
+ if (tracker.isEmpty())
+ return;
+
+ const QVector<QRhiPassResourceTracker::Buffer> *buffers = tracker.buffers();
+ for (const QRhiPassResourceTracker::Buffer &b : *buffers) {
+ QVkBuffer *bufD = QRHI_RES(QVkBuffer, b.buf);
+ VkAccessFlags access = toVkAccess(b.access);
+ VkPipelineStageFlags stage = toVkPipelineStage(b.stage);
+ QVkBuffer::UsageState s = toVkBufferUsageState(b.stateAtPassBegin);
+ if (!s.stage)
+ continue;
+ if (s.access == access && s.stage == stage) {
+ if (!accessIsWrite(access))
+ continue;
+ }
+ VkBufferMemoryBarrier bufMemBarrier;
+ memset(&bufMemBarrier, 0, sizeof(bufMemBarrier));
+ bufMemBarrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
+ bufMemBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+ bufMemBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+ bufMemBarrier.srcAccessMask = s.access;
+ bufMemBarrier.dstAccessMask = access;
+ bufMemBarrier.buffer = bufD->buffers[b.slot];
+ bufMemBarrier.size = VK_WHOLE_SIZE;
+ df->vkCmdPipelineBarrier(cbD->cb, s.stage, stage, 0,
+ 0, nullptr,
+ 1, &bufMemBarrier,
+ 0, nullptr);
+ }
+
+ const QVector<QRhiPassResourceTracker::Texture> *textures = tracker.textures();
+ for (const QRhiPassResourceTracker::Texture &t : *textures) {
+ QVkTexture *texD = QRHI_RES(QVkTexture, t.tex);
+ VkImageLayout layout = toVkLayout(t.access);
+ VkAccessFlags access = toVkAccess(t.access);
+ VkPipelineStageFlags stage = toVkPipelineStage(t.stage);
+ QVkTexture::UsageState s = toVkTextureUsageState(t.stateAtPassBegin);
+ if (s.access == access && s.stage == stage && s.layout == layout) {
+ if (!accessIsWrite(access))
+ continue;
+ }
+ VkImageMemoryBarrier barrier;
+ memset(&barrier, 0, sizeof(barrier));
+ barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
+ barrier.subresourceRange.aspectMask = !isDepthTextureFormat(texD->m_format)
+ ? VK_IMAGE_ASPECT_COLOR_BIT : VK_IMAGE_ASPECT_DEPTH_BIT;
+ barrier.subresourceRange.baseMipLevel = 0;
+ barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS;
+ barrier.subresourceRange.baseArrayLayer = 0;
+ barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS;
+ barrier.oldLayout = s.layout; // new textures have this set to PREINITIALIZED
+ barrier.newLayout = layout;
+ barrier.srcAccessMask = s.access; // may be 0 but that's fine
+ barrier.dstAccessMask = access;
+ barrier.image = texD->image;
+ VkPipelineStageFlags srcStage = s.stage;
+ // stage mask cannot be 0
+ if (!srcStage)
+ srcStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
+ df->vkCmdPipelineBarrier(cbD->cb, srcStage, stage, 0,
+ 0, nullptr,
+ 0, nullptr,
+ 1, &barrier);
+ }
+}
+
+QRhiSwapChain *QRhiVulkan::createSwapChain()
+{
+ return new QVkSwapChain(this);
+}
+
+QRhiBuffer *QRhiVulkan::createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, int size)
+{
+ return new QVkBuffer(this, type, usage, size);
+}
+
+int QRhiVulkan::ubufAlignment() const
+{
+ return ubufAlign; // typically 256 (bytes)
+}
+
+bool QRhiVulkan::isYUpInFramebuffer() const
+{
+ return false;
+}
+
+bool QRhiVulkan::isYUpInNDC() const
+{
+ return false;
+}
+
+bool QRhiVulkan::isClipDepthZeroToOne() const
+{
+ return true;
+}
+
+QMatrix4x4 QRhiVulkan::clipSpaceCorrMatrix() const
+{
+ // See https://matthewwellings.com/blog/the-new-vulkan-coordinate-system/
+
+ static QMatrix4x4 m;
+ if (m.isIdentity()) {
+ // NB the ctor takes row-major
+ m = QMatrix4x4(1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, -1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 0.5f, 0.5f,
+ 0.0f, 0.0f, 0.0f, 1.0f);
+ }
+ return m;
+}
+
+bool QRhiVulkan::isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const
+{
+ VkPhysicalDeviceFeatures features;
+ f->vkGetPhysicalDeviceFeatures(physDev, &features);
+
+ // Note that with some SDKs the validation layer gives an odd warning about
+ // BC not being supported, even when our check here succeeds. Not much we
+ // can do about that.
+ if (format >= QRhiTexture::BC1 && format <= QRhiTexture::BC7) {
+ if (!features.textureCompressionBC)
+ return false;
+ }
+
+ if (format >= QRhiTexture::ETC2_RGB8 && format <= QRhiTexture::ETC2_RGBA8) {
+ if (!features.textureCompressionETC2)
+ return false;
+ }
+
+ if (format >= QRhiTexture::ASTC_4x4 && format <= QRhiTexture::ASTC_12x12) {
+ if (!features.textureCompressionASTC_LDR)
+ return false;
+ }
+
+ VkFormat vkformat = toVkTextureFormat(format, flags);
+ VkFormatProperties props;
+ f->vkGetPhysicalDeviceFormatProperties(physDev, vkformat, &props);
+ return (props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) != 0;
+}
+
+bool QRhiVulkan::isFeatureSupported(QRhi::Feature feature) const
+{
+ switch (feature) {
+ case QRhi::MultisampleTexture:
+ return true;
+ case QRhi::MultisampleRenderBuffer:
+ return true;
+ case QRhi::DebugMarkers:
+ return debugMarkersAvailable;
+ case QRhi::Timestamps:
+ return timestampValidBits != 0;
+ case QRhi::Instancing:
+ return true;
+ case QRhi::CustomInstanceStepRate:
+ return vertexAttribDivisorAvailable;
+ case QRhi::PrimitiveRestart:
+ return true;
+ case QRhi::NonDynamicUniformBuffers:
+ return true;
+ case QRhi::NonFourAlignedEffectiveIndexBufferOffset:
+ return true;
+ case QRhi::NPOTTextureRepeat:
+ return true;
+ case QRhi::RedOrAlpha8IsRed:
+ return true;
+ case QRhi::ElementIndexUint:
+ return true;
+ default:
+ Q_UNREACHABLE();
+ return false;
+ }
+}
+
+int QRhiVulkan::resourceLimit(QRhi::ResourceLimit limit) const
+{
+ switch (limit) {
+ case QRhi::TextureSizeMin:
+ return 1;
+ case QRhi::TextureSizeMax:
+ return physDevProperties.limits.maxImageDimension2D;
+ case QRhi::MaxColorAttachments:
+ return physDevProperties.limits.maxColorAttachments;
+ case QRhi::FramesInFlight:
+ return QVK_FRAMES_IN_FLIGHT;
+ default:
+ Q_UNREACHABLE();
+ return 0;
+ }
+}
+
+const QRhiNativeHandles *QRhiVulkan::nativeHandles()
+{
+ return &nativeHandlesStruct;
+}
+
+void QRhiVulkan::sendVMemStatsToProfiler()
+{
+ QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
+ if (!rhiP)
+ return;
+
+ VmaStats stats;
+ vmaCalculateStats(toVmaAllocator(allocator), &stats);
+ QRHI_PROF_F(vmemStat(stats.total.blockCount, stats.total.allocationCount,
+ stats.total.usedBytes, stats.total.unusedBytes));
+}
+
+QRhiRenderBuffer *QRhiVulkan::createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize,
+ int sampleCount, QRhiRenderBuffer::Flags flags)
+{
+ return new QVkRenderBuffer(this, type, pixelSize, sampleCount, flags);
+}
+
+QRhiTexture *QRhiVulkan::createTexture(QRhiTexture::Format format, const QSize &pixelSize,
+ int sampleCount, QRhiTexture::Flags flags)
+{
+ return new QVkTexture(this, format, pixelSize, sampleCount, flags);
+}
+
+QRhiSampler *QRhiVulkan::createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
+ QRhiSampler::Filter mipmapMode,
+ QRhiSampler::AddressMode u, QRhiSampler::AddressMode v)
+{
+ return new QVkSampler(this, magFilter, minFilter, mipmapMode, u, v);
+}
+
+QRhiTextureRenderTarget *QRhiVulkan::createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
+ QRhiTextureRenderTarget::Flags flags)
+{
+ return new QVkTextureRenderTarget(this, desc, flags);
+}
+
+QRhiGraphicsPipeline *QRhiVulkan::createGraphicsPipeline()
+{
+ return new QVkGraphicsPipeline(this);
+}
+
+QRhiShaderResourceBindings *QRhiVulkan::createShaderResourceBindings()
+{
+ return new QVkShaderResourceBindings(this);
+}
+
+void QRhiVulkan::setGraphicsPipeline(QRhiCommandBuffer *cb, QRhiGraphicsPipeline *ps)
+{
+ Q_ASSERT(inPass);
+ QVkGraphicsPipeline *psD = QRHI_RES(QVkGraphicsPipeline, ps);
+ Q_ASSERT(psD->pipeline);
+ QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
+
+ if (cbD->currentPipeline != ps || cbD->currentPipelineGeneration != psD->generation) {
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::BindPipeline;
+ cmd.args.bindPipeline.bindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
+ cmd.args.bindPipeline.pipeline = psD->pipeline;
+ cbD->commands.append(cmd);
+
+ cbD->currentPipeline = ps;
+ cbD->currentPipelineGeneration = psD->generation;
+ }
+
+ psD->lastActiveFrameSlot = currentFrameSlot;
+}
+
+QRhiPassResourceTracker::BufferStage toPassTrackerBufferStage(QRhiShaderResourceBinding::StageFlags stages)
+{
+ // pick the earlier stage (as this is going to be dstAccessMask)
+ if (stages.testFlag(QRhiShaderResourceBinding::VertexStage))
+ return QRhiPassResourceTracker::BufVertexStage;
+ if (stages.testFlag(QRhiShaderResourceBinding::FragmentStage))
+ return QRhiPassResourceTracker::BufFragmentStage;
+
+ Q_UNREACHABLE();
+ return QRhiPassResourceTracker::BufVertexStage;
+}
+
+QRhiPassResourceTracker::TextureStage toPassTrackerTextureStage(QRhiShaderResourceBinding::StageFlags stages)
+{
+ // pick the earlier stage (as this is going to be dstAccessMask)
+ if (stages.testFlag(QRhiShaderResourceBinding::VertexStage))
+ return QRhiPassResourceTracker::TexVertexStage;
+ if (stages.testFlag(QRhiShaderResourceBinding::FragmentStage))
+ return QRhiPassResourceTracker::TexFragmentStage;
+
+ Q_UNREACHABLE();
+ return QRhiPassResourceTracker::TexVertexStage;
+}
+
+void QRhiVulkan::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBindings *srb,
+ int dynamicOffsetCount,
+ const QRhiCommandBuffer::DynamicOffset *dynamicOffsets)
+{
+ Q_ASSERT(inPass);
+
+ QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
+ Q_ASSERT(cbD->currentPipeline);
+ QVkGraphicsPipeline *psD = QRHI_RES(QVkGraphicsPipeline, cbD->currentPipeline);
+
+ if (!srb)
+ srb = psD->m_shaderResourceBindings;
+
+ QVkShaderResourceBindings *srbD = QRHI_RES(QVkShaderResourceBindings, srb);
+ bool hasSlottedResourceInSrb = false;
+ bool hasDynamicOffsetInSrb = false;
+
+ for (const QRhiShaderResourceBinding &binding : qAsConst(srbD->sortedBindings)) {
+ const QRhiShaderResourceBindingPrivate *b = QRhiShaderResourceBindingPrivate::get(&binding);
+ switch (b->type) {
+ case QRhiShaderResourceBinding::UniformBuffer:
+ if (QRHI_RES(QVkBuffer, b->u.ubuf.buf)->m_type == QRhiBuffer::Dynamic)
+ hasSlottedResourceInSrb = true;
+ if (b->u.ubuf.hasDynamicOffset)
+ hasDynamicOffsetInSrb = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ const int descSetIdx = hasSlottedResourceInSrb ? currentFrameSlot : 0;
+ bool rewriteDescSet = false;
+
+ // Do host writes and mark referenced shader resources as in-use.
+ // Also prepare to ensure the descriptor set we are going to bind refers to up-to-date Vk objects.
+ for (int i = 0, ie = srbD->sortedBindings.count(); i != ie; ++i) {
+ const QRhiShaderResourceBindingPrivate *b = QRhiShaderResourceBindingPrivate::get(&srbD->sortedBindings[i]);
+ QVkShaderResourceBindings::BoundResourceData &bd(srbD->boundResourceData[descSetIdx][i]);
+ QRhiPassResourceTracker &passResTracker(cbD->passResTrackers[cbD->currentPassResTrackerIndex]);
+ switch (b->type) {
+ case QRhiShaderResourceBinding::UniformBuffer:
+ {
+ QVkBuffer *bufD = QRHI_RES(QVkBuffer, b->u.ubuf.buf);
+ Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer));
+
+ if (bufD->m_type == QRhiBuffer::Dynamic)
+ executeBufferHostWritesForCurrentFrame(bufD);
+
+ bufD->lastActiveFrameSlot = currentFrameSlot;
+ trackedRegisterBuffer(&passResTracker, bufD, currentFrameSlot,
+ QRhiPassResourceTracker::BufUniformRead,
+ toPassTrackerBufferStage(b->stage));
+
+ // Check both the "local" id (the generation counter) and the
+ // global id. The latter is relevant when a newly allocated
+ // QRhiResource ends up with the same pointer as a previous one.
+ // (and that previous one could have been in an srb...)
+ if (bufD->generation != bd.ubuf.generation || bufD->m_id != bd.ubuf.id) {
+ rewriteDescSet = true;
+ bd.ubuf.id = bufD->m_id;
+ bd.ubuf.generation = bufD->generation;
+ }
+ }
+ break;
+ case QRhiShaderResourceBinding::SampledTexture:
+ {
+ QVkTexture *texD = QRHI_RES(QVkTexture, b->u.stex.tex);
+ QVkSampler *samplerD = QRHI_RES(QVkSampler, b->u.stex.sampler);
+ texD->lastActiveFrameSlot = currentFrameSlot;
+ samplerD->lastActiveFrameSlot = currentFrameSlot;
+ trackedRegisterTexture(&passResTracker, texD,
+ QRhiPassResourceTracker::TexSample,
+ toPassTrackerTextureStage(b->stage));
+
+ if (texD->generation != bd.stex.texGeneration
+ || texD->m_id != bd.stex.texId
+ || samplerD->generation != bd.stex.samplerGeneration
+ || samplerD->m_id != bd.stex.samplerId)
+ {
+ rewriteDescSet = true;
+ bd.stex.texId = texD->m_id;
+ bd.stex.texGeneration = texD->generation;
+ bd.stex.samplerId = samplerD->m_id;
+ bd.stex.samplerGeneration = samplerD->generation;
+ }
+ }
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ }
+
+ // write descriptor sets, if needed
+ if (rewriteDescSet)
+ updateShaderResourceBindings(srb, descSetIdx);
+
+ // make sure the descriptors for the correct slot will get bound.
+ // also, dynamic offsets always need a bind.
+ const bool forceRebind = (hasSlottedResourceInSrb && cbD->currentDescSetSlot != descSetIdx) || hasDynamicOffsetInSrb;
+
+ if (forceRebind || rewriteDescSet || cbD->currentSrb != srb || cbD->currentSrbGeneration != srbD->generation) {
+ QVarLengthArray<uint32_t, 4> dynOfs;
+ if (hasDynamicOffsetInSrb) {
+ // Filling out dynOfs based on the sorted bindings is important
+ // because dynOfs has to be ordered based on the binding numbers,
+ // and neither srb nor dynamicOffsets has any such ordering
+ // requirement.
+ for (const QRhiShaderResourceBinding &binding : qAsConst(srbD->sortedBindings)) {
+ const QRhiShaderResourceBindingPrivate *b = QRhiShaderResourceBindingPrivate::get(&binding);
+ if (b->type == QRhiShaderResourceBinding::UniformBuffer && b->u.ubuf.hasDynamicOffset) {
+ uint32_t offset = 0;
+ for (int i = 0; i < dynamicOffsetCount; ++i) {
+ const QRhiCommandBuffer::DynamicOffset &dynOfs(dynamicOffsets[i]);
+ if (dynOfs.first == b->binding) {
+ offset = dynOfs.second;
+ break;
+ }
+ }
+ dynOfs.append(offset); // use 0 if dynamicOffsets did not contain this binding
+ }
+ }
+ }
+
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::BindDescriptorSet;
+ cmd.args.bindDescriptorSet.bindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
+ cmd.args.bindDescriptorSet.pipelineLayout = psD->layout;
+ cmd.args.bindDescriptorSet.descSet = srbD->descSets[descSetIdx];
+ cmd.args.bindDescriptorSet.dynamicOffsetCount = dynOfs.count();
+ cmd.args.bindDescriptorSet.dynamicOffsetIndex = cbD->pools.dynamicOffset.count();
+ cbD->pools.dynamicOffset.append(dynOfs.constData(), dynOfs.count());
+ cbD->commands.append(cmd);
+
+ cbD->currentSrb = srb;
+ cbD->currentSrbGeneration = srbD->generation;
+ cbD->currentDescSetSlot = descSetIdx;
+ }
+
+ srbD->lastActiveFrameSlot = currentFrameSlot;
+}
+
+void QRhiVulkan::setVertexInput(QRhiCommandBuffer *cb,
+ int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings,
+ QRhiBuffer *indexBuf, quint32 indexOffset, QRhiCommandBuffer::IndexFormat indexFormat)
+{
+ Q_ASSERT(inPass);
+ QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
+ QRhiPassResourceTracker &passResTracker(cbD->passResTrackers[cbD->currentPassResTrackerIndex]);
+
+ bool needsBindVBuf = false;
+ for (int i = 0; i < bindingCount; ++i) {
+ const int inputSlot = startBinding + i;
+ QVkBuffer *bufD = QRHI_RES(QVkBuffer, bindings[i].first);
+ Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::VertexBuffer));
+ bufD->lastActiveFrameSlot = currentFrameSlot;
+ if (bufD->m_type == QRhiBuffer::Dynamic)
+ executeBufferHostWritesForCurrentFrame(bufD);
+
+ const VkBuffer vkvertexbuf = bufD->buffers[bufD->m_type == QRhiBuffer::Dynamic ? currentFrameSlot : 0];
+ if (cbD->currentVertexBuffers[inputSlot] != vkvertexbuf
+ || cbD->currentVertexOffsets[inputSlot] != bindings[i].second)
+ {
+ needsBindVBuf = true;
+ cbD->currentVertexBuffers[inputSlot] = vkvertexbuf;
+ cbD->currentVertexOffsets[inputSlot] = bindings[i].second;
+ }
+ }
+
+ if (needsBindVBuf) {
+ QVarLengthArray<VkBuffer, 4> bufs;
+ QVarLengthArray<VkDeviceSize, 4> ofs;
+ for (int i = 0; i < bindingCount; ++i) {
+ QVkBuffer *bufD = QRHI_RES(QVkBuffer, bindings[i].first);
+ const int slot = bufD->m_type == QRhiBuffer::Dynamic ? currentFrameSlot : 0;
+ bufs.append(bufD->buffers[slot]);
+ ofs.append(bindings[i].second);
+ trackedRegisterBuffer(&passResTracker, bufD, slot,
+ QRhiPassResourceTracker::BufVertexInput,
+ QRhiPassResourceTracker::BufVertexInputStage);
+ }
+
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::BindVertexBuffer;
+ cmd.args.bindVertexBuffer.startBinding = startBinding;
+ cmd.args.bindVertexBuffer.count = bufs.count();
+ cmd.args.bindVertexBuffer.vertexBufferIndex = cbD->pools.vertexBuffer.count();
+ cbD->pools.vertexBuffer.append(bufs.constData(), bufs.count());
+ cmd.args.bindVertexBuffer.vertexBufferOffsetIndex = cbD->pools.vertexBufferOffset.count();
+ cbD->pools.vertexBufferOffset.append(ofs.constData(), ofs.count());
+ cbD->commands.append(cmd);
+ }
+
+ if (indexBuf) {
+ QVkBuffer *ibufD = QRHI_RES(QVkBuffer, indexBuf);
+ Q_ASSERT(ibufD->m_usage.testFlag(QRhiBuffer::IndexBuffer));
+ ibufD->lastActiveFrameSlot = currentFrameSlot;
+ if (ibufD->m_type == QRhiBuffer::Dynamic)
+ executeBufferHostWritesForCurrentFrame(ibufD);
+
+ const int slot = ibufD->m_type == QRhiBuffer::Dynamic ? currentFrameSlot : 0;
+ const VkBuffer vkindexbuf = ibufD->buffers[slot];
+ const VkIndexType type = indexFormat == QRhiCommandBuffer::IndexUInt16 ? VK_INDEX_TYPE_UINT16
+ : VK_INDEX_TYPE_UINT32;
+
+ if (cbD->currentIndexBuffer != vkindexbuf
+ || cbD->currentIndexOffset != indexOffset
+ || cbD->currentIndexFormat != type)
+ {
+ cbD->currentIndexBuffer = vkindexbuf;
+ cbD->currentIndexOffset = indexOffset;
+ cbD->currentIndexFormat = type;
+
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::BindIndexBuffer;
+ cmd.args.bindIndexBuffer.buf = vkindexbuf;
+ cmd.args.bindIndexBuffer.ofs = indexOffset;
+ cmd.args.bindIndexBuffer.type = type;
+ cbD->commands.append(cmd);
+
+ trackedRegisterBuffer(&passResTracker, ibufD, slot,
+ QRhiPassResourceTracker::BufIndexRead,
+ QRhiPassResourceTracker::BufVertexInputStage);
+ }
+ }
+}
+
+void QRhiVulkan::setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport)
+{
+ Q_ASSERT(inPass);
+ QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
+ Q_ASSERT(cbD->currentPipeline && cbD->currentTarget);
+ const QSize outputSize = cbD->currentTarget->pixelSize();
+
+ // x,y is top-left in VkViewport but bottom-left in QRhiViewport
+ float x, y, w, h;
+ if (!qrhi_toTopLeftRenderTargetRect(outputSize, viewport.viewport(), &x, &y, &w, &h))
+ return;
+
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::SetViewport;
+ VkViewport *vp = &cmd.args.setViewport.viewport;
+ vp->x = x;
+ vp->y = y;
+ vp->width = w;
+ vp->height = h;
+ vp->minDepth = viewport.minDepth();
+ vp->maxDepth = viewport.maxDepth();
+ cbD->commands.append(cmd);
+
+ if (!QRHI_RES(QVkGraphicsPipeline, cbD->currentPipeline)->m_flags.testFlag(QRhiGraphicsPipeline::UsesScissor)) {
+ cmd.cmd = QVkCommandBuffer::Command::SetScissor;
+ VkRect2D *s = &cmd.args.setScissor.scissor;
+ s->offset.x = x;
+ s->offset.y = y;
+ s->extent.width = w;
+ s->extent.height = h;
+ cbD->commands.append(cmd);
+ }
+}
+
+void QRhiVulkan::setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor)
+{
+ Q_ASSERT(inPass);
+ QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
+ Q_ASSERT(cbD->currentPipeline && cbD->currentTarget);
+ Q_ASSERT(QRHI_RES(QVkGraphicsPipeline, cbD->currentPipeline)->m_flags.testFlag(QRhiGraphicsPipeline::UsesScissor));
+ const QSize outputSize = cbD->currentTarget->pixelSize();
+
+ // x,y is top-left in VkRect2D but bottom-left in QRhiScissor
+ int x, y, w, h;
+ if (!qrhi_toTopLeftRenderTargetRect(outputSize, scissor.scissor(), &x, &y, &w, &h))
+ return;
+
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::SetScissor;
+ VkRect2D *s = &cmd.args.setScissor.scissor;
+ s->offset.x = x;
+ s->offset.y = y;
+ s->extent.width = w;
+ s->extent.height = h;
+ cbD->commands.append(cmd);
+}
+
+void QRhiVulkan::setBlendConstants(QRhiCommandBuffer *cb, const QColor &c)
+{
+ Q_ASSERT(inPass);
+ QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::SetBlendConstants;
+ cmd.args.setBlendConstants.c[0] = c.redF();
+ cmd.args.setBlendConstants.c[1] = c.greenF();
+ cmd.args.setBlendConstants.c[2] = c.blueF();
+ cmd.args.setBlendConstants.c[3] = c.alphaF();
+ cbD->commands.append(cmd);
+}
+
+void QRhiVulkan::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue)
+{
+ Q_ASSERT(inPass);
+ QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::SetStencilRef;
+ cmd.args.setStencilRef.ref = refValue;
+ cbD->commands.append(cmd);
+}
+
+void QRhiVulkan::draw(QRhiCommandBuffer *cb, quint32 vertexCount,
+ quint32 instanceCount, quint32 firstVertex, quint32 firstInstance)
+{
+ Q_ASSERT(inPass);
+ QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::Draw;
+ cmd.args.draw.vertexCount = vertexCount;
+ cmd.args.draw.instanceCount = instanceCount;
+ cmd.args.draw.firstVertex = firstVertex;
+ cmd.args.draw.firstInstance = firstInstance;
+ cbD->commands.append(cmd);
+}
+
+void QRhiVulkan::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
+ quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance)
+{
+ Q_ASSERT(inPass);
+ QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::DrawIndexed;
+ cmd.args.drawIndexed.indexCount = indexCount;
+ cmd.args.drawIndexed.instanceCount = instanceCount;
+ cmd.args.drawIndexed.firstIndex = firstIndex;
+ cmd.args.drawIndexed.vertexOffset = vertexOffset;
+ cmd.args.drawIndexed.firstInstance = firstInstance;
+ cbD->commands.append(cmd);
+}
+
+void QRhiVulkan::debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name)
+{
+ if (!debugMarkers || !debugMarkersAvailable)
+ return;
+
+ VkDebugMarkerMarkerInfoEXT marker;
+ memset(&marker, 0, sizeof(marker));
+ marker.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT;
+
+ QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::DebugMarkerBegin;
+ cmd.args.debugMarkerBegin.marker = marker;
+ cmd.args.debugMarkerBegin.markerNameIndex = cbD->pools.debugMarkerName.count();
+ cbD->pools.debugMarkerName.append(name);
+ cbD->commands.append(cmd);
+}
+
+void QRhiVulkan::debugMarkEnd(QRhiCommandBuffer *cb)
+{
+ if (!debugMarkers || !debugMarkersAvailable)
+ return;
+
+ QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::DebugMarkerEnd;
+ cbD->commands.append(cmd);
+}
+
+void QRhiVulkan::debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg)
+{
+ if (!debugMarkers || !debugMarkersAvailable)
+ return;
+
+ VkDebugMarkerMarkerInfoEXT marker;
+ memset(&marker, 0, sizeof(marker));
+ marker.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT;
+ marker.pMarkerName = msg.constData();
+
+ QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::DebugMarkerInsert;
+ cmd.args.debugMarkerInsert.marker = marker;
+ cbD->commands.append(cmd);
+}
+
+const QRhiNativeHandles *QRhiVulkan::nativeHandles(QRhiCommandBuffer *cb)
+{
+ return QRHI_RES(QVkCommandBuffer, cb)->nativeHandles();
+}
+
+void QRhiVulkan::beginExternal(QRhiCommandBuffer *cb)
+{
+ Q_UNUSED(cb);
+}
+
+void QRhiVulkan::endExternal(QRhiCommandBuffer *cb)
+{
+ QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
+ cbD->resetCachedState();
+}
+
+void QRhiVulkan::setObjectName(uint64_t object, VkDebugReportObjectTypeEXT type, const QByteArray &name, int slot)
+{
+ if (!debugMarkers || !debugMarkersAvailable || name.isEmpty())
+ return;
+
+ VkDebugMarkerObjectNameInfoEXT nameInfo;
+ memset(&nameInfo, 0, sizeof(nameInfo));
+ nameInfo.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_NAME_INFO_EXT;
+ nameInfo.objectType = type;
+ nameInfo.object = object;
+ QByteArray decoratedName = name;
+ if (slot >= 0) {
+ decoratedName += '/';
+ decoratedName += QByteArray::number(slot);
+ }
+ nameInfo.pObjectName = decoratedName.constData();
+ vkDebugMarkerSetObjectName(dev, &nameInfo);
+}
+
+static inline VkBufferUsageFlagBits toVkBufferUsage(QRhiBuffer::UsageFlags usage)
+{
+ int u = 0;
+ if (usage.testFlag(QRhiBuffer::VertexBuffer))
+ u |= VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
+ if (usage.testFlag(QRhiBuffer::IndexBuffer))
+ u |= VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
+ if (usage.testFlag(QRhiBuffer::UniformBuffer))
+ u |= VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
+ return VkBufferUsageFlagBits(u);
+}
+
+static inline VkFilter toVkFilter(QRhiSampler::Filter f)
+{
+ switch (f) {
+ case QRhiSampler::Nearest:
+ return VK_FILTER_NEAREST;
+ case QRhiSampler::Linear:
+ return VK_FILTER_LINEAR;
+ default:
+ Q_UNREACHABLE();
+ return VK_FILTER_NEAREST;
+ }
+}
+
+static inline VkSamplerMipmapMode toVkMipmapMode(QRhiSampler::Filter f)
+{
+ switch (f) {
+ case QRhiSampler::None:
+ return VK_SAMPLER_MIPMAP_MODE_NEAREST;
+ case QRhiSampler::Nearest:
+ return VK_SAMPLER_MIPMAP_MODE_NEAREST;
+ case QRhiSampler::Linear:
+ return VK_SAMPLER_MIPMAP_MODE_LINEAR;
+ default:
+ Q_UNREACHABLE();
+ return VK_SAMPLER_MIPMAP_MODE_NEAREST;
+ }
+}
+
+static inline VkSamplerAddressMode toVkAddressMode(QRhiSampler::AddressMode m)
+{
+ switch (m) {
+ case QRhiSampler::Repeat:
+ return VK_SAMPLER_ADDRESS_MODE_REPEAT;
+ case QRhiSampler::ClampToEdge:
+ return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
+ case QRhiSampler::Border:
+ return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
+ case QRhiSampler::Mirror:
+ return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
+ case QRhiSampler::MirrorOnce:
+ return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE;
+ default:
+ Q_UNREACHABLE();
+ return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
+ }
+}
+
+static inline VkShaderStageFlagBits toVkShaderStage(QRhiGraphicsShaderStage::Type type)
+{
+ switch (type) {
+ case QRhiGraphicsShaderStage::Vertex:
+ return VK_SHADER_STAGE_VERTEX_BIT;
+ case QRhiGraphicsShaderStage::Fragment:
+ return VK_SHADER_STAGE_FRAGMENT_BIT;
+ default:
+ Q_UNREACHABLE();
+ return VK_SHADER_STAGE_VERTEX_BIT;
+ }
+}
+
+static inline VkFormat toVkAttributeFormat(QRhiVertexInputAttribute::Format format)
+{
+ switch (format) {
+ case QRhiVertexInputAttribute::Float4:
+ return VK_FORMAT_R32G32B32A32_SFLOAT;
+ case QRhiVertexInputAttribute::Float3:
+ return VK_FORMAT_R32G32B32_SFLOAT;
+ case QRhiVertexInputAttribute::Float2:
+ return VK_FORMAT_R32G32_SFLOAT;
+ case QRhiVertexInputAttribute::Float:
+ return VK_FORMAT_R32_SFLOAT;
+ case QRhiVertexInputAttribute::UNormByte4:
+ return VK_FORMAT_R8G8B8A8_UNORM;
+ case QRhiVertexInputAttribute::UNormByte2:
+ return VK_FORMAT_R8G8_UNORM;
+ case QRhiVertexInputAttribute::UNormByte:
+ return VK_FORMAT_R8_UNORM;
+ default:
+ Q_UNREACHABLE();
+ return VK_FORMAT_R32G32B32A32_SFLOAT;
+ }
+}
+
+static inline VkPrimitiveTopology toVkTopology(QRhiGraphicsPipeline::Topology t)
+{
+ switch (t) {
+ case QRhiGraphicsPipeline::Triangles:
+ return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
+ case QRhiGraphicsPipeline::TriangleStrip:
+ return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
+ case QRhiGraphicsPipeline::Lines:
+ return VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
+ case QRhiGraphicsPipeline::LineStrip:
+ return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP;
+ case QRhiGraphicsPipeline::Points:
+ return VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
+ default:
+ Q_UNREACHABLE();
+ return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
+ }
+}
+
+static inline VkCullModeFlags toVkCullMode(QRhiGraphicsPipeline::CullMode c)
+{
+ switch (c) {
+ case QRhiGraphicsPipeline::None:
+ return VK_CULL_MODE_NONE;
+ case QRhiGraphicsPipeline::Front:
+ return VK_CULL_MODE_FRONT_BIT;
+ case QRhiGraphicsPipeline::Back:
+ return VK_CULL_MODE_BACK_BIT;
+ default:
+ Q_UNREACHABLE();
+ return VK_CULL_MODE_NONE;
+ }
+}
+
+static inline VkFrontFace toVkFrontFace(QRhiGraphicsPipeline::FrontFace f)
+{
+ switch (f) {
+ case QRhiGraphicsPipeline::CCW:
+ return VK_FRONT_FACE_COUNTER_CLOCKWISE;
+ case QRhiGraphicsPipeline::CW:
+ return VK_FRONT_FACE_CLOCKWISE;
+ default:
+ Q_UNREACHABLE();
+ return VK_FRONT_FACE_COUNTER_CLOCKWISE;
+ }
+}
+
+static inline VkColorComponentFlags toVkColorComponents(QRhiGraphicsPipeline::ColorMask c)
+{
+ int f = 0;
+ if (c.testFlag(QRhiGraphicsPipeline::R))
+ f |= VK_COLOR_COMPONENT_R_BIT;
+ if (c.testFlag(QRhiGraphicsPipeline::G))
+ f |= VK_COLOR_COMPONENT_G_BIT;
+ if (c.testFlag(QRhiGraphicsPipeline::B))
+ f |= VK_COLOR_COMPONENT_B_BIT;
+ if (c.testFlag(QRhiGraphicsPipeline::A))
+ f |= VK_COLOR_COMPONENT_A_BIT;
+ return VkColorComponentFlags(f);
+}
+
+static inline VkBlendFactor toVkBlendFactor(QRhiGraphicsPipeline::BlendFactor f)
+{
+ switch (f) {
+ case QRhiGraphicsPipeline::Zero:
+ return VK_BLEND_FACTOR_ZERO;
+ case QRhiGraphicsPipeline::One:
+ return VK_BLEND_FACTOR_ONE;
+ case QRhiGraphicsPipeline::SrcColor:
+ return VK_BLEND_FACTOR_SRC_COLOR;
+ case QRhiGraphicsPipeline::OneMinusSrcColor:
+ return VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR;
+ case QRhiGraphicsPipeline::DstColor:
+ return VK_BLEND_FACTOR_DST_COLOR;
+ case QRhiGraphicsPipeline::OneMinusDstColor:
+ return VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR;
+ case QRhiGraphicsPipeline::SrcAlpha:
+ return VK_BLEND_FACTOR_SRC_ALPHA;
+ case QRhiGraphicsPipeline::OneMinusSrcAlpha:
+ return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
+ case QRhiGraphicsPipeline::DstAlpha:
+ return VK_BLEND_FACTOR_DST_ALPHA;
+ case QRhiGraphicsPipeline::OneMinusDstAlpha:
+ return VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA;
+ case QRhiGraphicsPipeline::ConstantColor:
+ return VK_BLEND_FACTOR_CONSTANT_COLOR;
+ case QRhiGraphicsPipeline::OneMinusConstantColor:
+ return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR;
+ case QRhiGraphicsPipeline::ConstantAlpha:
+ return VK_BLEND_FACTOR_CONSTANT_ALPHA;
+ case QRhiGraphicsPipeline::OneMinusConstantAlpha:
+ return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA;
+ case QRhiGraphicsPipeline::SrcAlphaSaturate:
+ return VK_BLEND_FACTOR_SRC_ALPHA_SATURATE;
+ case QRhiGraphicsPipeline::Src1Color:
+ return VK_BLEND_FACTOR_SRC1_COLOR;
+ case QRhiGraphicsPipeline::OneMinusSrc1Color:
+ return VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR;
+ case QRhiGraphicsPipeline::Src1Alpha:
+ return VK_BLEND_FACTOR_SRC1_ALPHA;
+ case QRhiGraphicsPipeline::OneMinusSrc1Alpha:
+ return VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA;
+ default:
+ Q_UNREACHABLE();
+ return VK_BLEND_FACTOR_ZERO;
+ }
+}
+
+static inline VkBlendOp toVkBlendOp(QRhiGraphicsPipeline::BlendOp op)
+{
+ switch (op) {
+ case QRhiGraphicsPipeline::Add:
+ return VK_BLEND_OP_ADD;
+ case QRhiGraphicsPipeline::Subtract:
+ return VK_BLEND_OP_SUBTRACT;
+ case QRhiGraphicsPipeline::ReverseSubtract:
+ return VK_BLEND_OP_REVERSE_SUBTRACT;
+ case QRhiGraphicsPipeline::Min:
+ return VK_BLEND_OP_MIN;
+ case QRhiGraphicsPipeline::Max:
+ return VK_BLEND_OP_MAX;
+ default:
+ Q_UNREACHABLE();
+ return VK_BLEND_OP_ADD;
+ }
+}
+
+static inline VkCompareOp toVkCompareOp(QRhiGraphicsPipeline::CompareOp op)
+{
+ switch (op) {
+ case QRhiGraphicsPipeline::Never:
+ return VK_COMPARE_OP_NEVER;
+ case QRhiGraphicsPipeline::Less:
+ return VK_COMPARE_OP_LESS;
+ case QRhiGraphicsPipeline::Equal:
+ return VK_COMPARE_OP_EQUAL;
+ case QRhiGraphicsPipeline::LessOrEqual:
+ return VK_COMPARE_OP_LESS_OR_EQUAL;
+ case QRhiGraphicsPipeline::Greater:
+ return VK_COMPARE_OP_GREATER;
+ case QRhiGraphicsPipeline::NotEqual:
+ return VK_COMPARE_OP_NOT_EQUAL;
+ case QRhiGraphicsPipeline::GreaterOrEqual:
+ return VK_COMPARE_OP_GREATER_OR_EQUAL;
+ case QRhiGraphicsPipeline::Always:
+ return VK_COMPARE_OP_ALWAYS;
+ default:
+ Q_UNREACHABLE();
+ return VK_COMPARE_OP_ALWAYS;
+ }
+}
+
+static inline VkStencilOp toVkStencilOp(QRhiGraphicsPipeline::StencilOp op)
+{
+ switch (op) {
+ case QRhiGraphicsPipeline::StencilZero:
+ return VK_STENCIL_OP_ZERO;
+ case QRhiGraphicsPipeline::Keep:
+ return VK_STENCIL_OP_KEEP;
+ case QRhiGraphicsPipeline::Replace:
+ return VK_STENCIL_OP_REPLACE;
+ case QRhiGraphicsPipeline::IncrementAndClamp:
+ return VK_STENCIL_OP_INCREMENT_AND_CLAMP;
+ case QRhiGraphicsPipeline::DecrementAndClamp:
+ return VK_STENCIL_OP_DECREMENT_AND_CLAMP;
+ case QRhiGraphicsPipeline::Invert:
+ return VK_STENCIL_OP_INVERT;
+ case QRhiGraphicsPipeline::IncrementAndWrap:
+ return VK_STENCIL_OP_INCREMENT_AND_WRAP;
+ case QRhiGraphicsPipeline::DecrementAndWrap:
+ return VK_STENCIL_OP_DECREMENT_AND_WRAP;
+ default:
+ Q_UNREACHABLE();
+ return VK_STENCIL_OP_KEEP;
+ }
+}
+
+static inline void fillVkStencilOpState(VkStencilOpState *dst, const QRhiGraphicsPipeline::StencilOpState &src)
+{
+ dst->failOp = toVkStencilOp(src.failOp);
+ dst->passOp = toVkStencilOp(src.passOp);
+ dst->depthFailOp = toVkStencilOp(src.depthFailOp);
+ dst->compareOp = toVkCompareOp(src.compareOp);
+}
+
+static inline VkDescriptorType toVkDescriptorType(const QRhiShaderResourceBindingPrivate *b)
+{
+ switch (b->type) {
+ case QRhiShaderResourceBinding::UniformBuffer:
+ return b->u.ubuf.hasDynamicOffset ? VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC
+ : VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+ case QRhiShaderResourceBinding::SampledTexture:
+ return VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+ default:
+ Q_UNREACHABLE();
+ return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+ }
+}
+
+static inline VkShaderStageFlags toVkShaderStageFlags(QRhiShaderResourceBinding::StageFlags stage)
+{
+ int s = 0;
+ if (stage.testFlag(QRhiShaderResourceBinding::VertexStage))
+ s |= VK_SHADER_STAGE_VERTEX_BIT;
+ if (stage.testFlag(QRhiShaderResourceBinding::FragmentStage))
+ s |= VK_SHADER_STAGE_FRAGMENT_BIT;
+ return VkShaderStageFlags(s);
+}
+
+static inline VkCompareOp toVkTextureCompareOp(QRhiSampler::CompareOp op)
+{
+ switch (op) {
+ case QRhiSampler::Never:
+ return VK_COMPARE_OP_NEVER;
+ case QRhiSampler::Less:
+ return VK_COMPARE_OP_LESS;
+ case QRhiSampler::Equal:
+ return VK_COMPARE_OP_EQUAL;
+ case QRhiSampler::LessOrEqual:
+ return VK_COMPARE_OP_LESS_OR_EQUAL;
+ case QRhiSampler::Greater:
+ return VK_COMPARE_OP_GREATER;
+ case QRhiSampler::NotEqual:
+ return VK_COMPARE_OP_NOT_EQUAL;
+ case QRhiSampler::GreaterOrEqual:
+ return VK_COMPARE_OP_GREATER_OR_EQUAL;
+ case QRhiSampler::Always:
+ return VK_COMPARE_OP_ALWAYS;
+ default:
+ Q_UNREACHABLE();
+ return VK_COMPARE_OP_NEVER;
+ }
+}
+
+QVkBuffer::QVkBuffer(QRhiImplementation *rhi, Type type, UsageFlags usage, int size)
+ : QRhiBuffer(rhi, type, usage, size)
+{
+ for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
+ buffers[i] = stagingBuffers[i] = VK_NULL_HANDLE;
+ allocations[i] = stagingAllocations[i] = nullptr;
+ }
+}
+
+QVkBuffer::~QVkBuffer()
+{
+ release();
+}
+
+void QVkBuffer::release()
+{
+ if (!buffers[0])
+ return;
+
+ QRhiVulkan::DeferredReleaseEntry e;
+ e.type = QRhiVulkan::DeferredReleaseEntry::Buffer;
+ e.lastActiveFrameSlot = lastActiveFrameSlot;
+
+ for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
+ e.buffer.buffers[i] = buffers[i];
+ e.buffer.allocations[i] = allocations[i];
+ e.buffer.stagingBuffers[i] = stagingBuffers[i];
+ e.buffer.stagingAllocations[i] = stagingAllocations[i];
+
+ buffers[i] = VK_NULL_HANDLE;
+ allocations[i] = nullptr;
+ stagingBuffers[i] = VK_NULL_HANDLE;
+ stagingAllocations[i] = nullptr;
+ pendingDynamicUpdates[i].clear();
+ }
+
+ QRHI_RES_RHI(QRhiVulkan);
+ rhiD->releaseQueue.append(e);
+
+ QRHI_PROF;
+ QRHI_PROF_F(releaseBuffer(this));
+
+ rhiD->unregisterResource(this);
+}
+
+bool QVkBuffer::build()
+{
+ if (buffers[0])
+ release();
+
+ const int nonZeroSize = m_size <= 0 ? 256 : m_size;
+
+ VkBufferCreateInfo bufferInfo;
+ memset(&bufferInfo, 0, sizeof(bufferInfo));
+ bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
+ bufferInfo.size = nonZeroSize;
+ bufferInfo.usage = toVkBufferUsage(m_usage);
+
+ VmaAllocationCreateInfo allocInfo;
+ memset(&allocInfo, 0, sizeof(allocInfo));
+
+ if (m_type == Dynamic) {
+#ifndef Q_OS_DARWIN // not for MoltenVK
+ // Keep mapped all the time. Essential f.ex. with some mobile GPUs,
+ // where mapping and unmapping an entire allocation every time updating
+ // a suballocated buffer presents a significant perf. hit.
+ allocInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
+#endif
+ // host visible, frequent changes
+ allocInfo.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;
+ } else {
+ allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
+ bufferInfo.usage |= VK_BUFFER_USAGE_TRANSFER_DST_BIT;
+ }
+
+ QRHI_RES_RHI(QRhiVulkan);
+ VkResult err = VK_SUCCESS;
+ for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
+ buffers[i] = VK_NULL_HANDLE;
+ allocations[i] = nullptr;
+ usageState[i].access = usageState[i].stage = 0;
+ if (i == 0 || m_type == Dynamic) {
+ VmaAllocation allocation;
+ err = vmaCreateBuffer(toVmaAllocator(rhiD->allocator), &bufferInfo, &allocInfo, &buffers[i], &allocation, nullptr);
+ if (err != VK_SUCCESS)
+ break;
+
+ allocations[i] = allocation;
+ if (m_type == Dynamic)
+ pendingDynamicUpdates[i].reserve(16);
+
+ rhiD->setObjectName(uint64_t(buffers[i]), VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_EXT, m_objectName,
+ m_type == Dynamic ? i : -1);
+ }
+ }
+
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create buffer: %d", err);
+ return false;
+ }
+
+ QRHI_PROF;
+ QRHI_PROF_F(newBuffer(this, nonZeroSize, m_type != Dynamic ? 1 : QVK_FRAMES_IN_FLIGHT, 0));
+
+ lastActiveFrameSlot = -1;
+ generation += 1;
+ rhiD->registerResource(this);
+ return true;
+}
+
+QVkRenderBuffer::QVkRenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize,
+ int sampleCount, Flags flags)
+ : QRhiRenderBuffer(rhi, type, pixelSize, sampleCount, flags)
+{
+}
+
+QVkRenderBuffer::~QVkRenderBuffer()
+{
+ release();
+ delete backingTexture;
+}
+
+void QVkRenderBuffer::release()
+{
+ if (!memory && !backingTexture)
+ return;
+
+ QRhiVulkan::DeferredReleaseEntry e;
+ e.type = QRhiVulkan::DeferredReleaseEntry::RenderBuffer;
+ e.lastActiveFrameSlot = lastActiveFrameSlot;
+
+ e.renderBuffer.memory = memory;
+ e.renderBuffer.image = image;
+ e.renderBuffer.imageView = imageView;
+
+ memory = VK_NULL_HANDLE;
+ image = VK_NULL_HANDLE;
+ imageView = VK_NULL_HANDLE;
+
+ if (backingTexture) {
+ Q_ASSERT(backingTexture->lastActiveFrameSlot == -1);
+ backingTexture->lastActiveFrameSlot = e.lastActiveFrameSlot;
+ backingTexture->release();
+ }
+
+ QRHI_RES_RHI(QRhiVulkan);
+ rhiD->releaseQueue.append(e);
+
+ QRHI_PROF;
+ QRHI_PROF_F(releaseRenderBuffer(this));
+
+ rhiD->unregisterResource(this);
+}
+
+bool QVkRenderBuffer::build()
+{
+ if (memory || backingTexture)
+ release();
+
+ if (m_pixelSize.isEmpty())
+ return false;
+
+ QRHI_RES_RHI(QRhiVulkan);
+ QRHI_PROF;
+ samples = rhiD->effectiveSampleCount(m_sampleCount);
+
+ switch (m_type) {
+ case QRhiRenderBuffer::Color:
+ {
+ if (!backingTexture) {
+ backingTexture = QRHI_RES(QVkTexture, rhiD->createTexture(QRhiTexture::RGBA8,
+ m_pixelSize,
+ m_sampleCount,
+ QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource));
+ } else {
+ backingTexture->setPixelSize(m_pixelSize);
+ backingTexture->setSampleCount(m_sampleCount);
+ }
+ backingTexture->setName(m_objectName);
+ if (!backingTexture->build())
+ return false;
+ vkformat = backingTexture->vkformat;
+ QRHI_PROF_F(newRenderBuffer(this, false, false, samples));
+ }
+ break;
+ case QRhiRenderBuffer::DepthStencil:
+ vkformat = rhiD->optimalDepthStencilFormat();
+ if (!rhiD->createTransientImage(vkformat,
+ m_pixelSize,
+ VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
+ VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT,
+ samples,
+ &memory,
+ &image,
+ &imageView,
+ 1))
+ {
+ return false;
+ }
+ rhiD->setObjectName(uint64_t(image), VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT, m_objectName);
+ QRHI_PROF_F(newRenderBuffer(this, true, false, samples));
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+
+ lastActiveFrameSlot = -1;
+ rhiD->registerResource(this);
+ return true;
+}
+
+QRhiTexture::Format QVkRenderBuffer::backingFormat() const
+{
+ return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat;
+}
+
+QVkTexture::QVkTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize,
+ int sampleCount, Flags flags)
+ : QRhiTexture(rhi, format, pixelSize, sampleCount, flags)
+{
+ for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
+ stagingBuffers[i] = VK_NULL_HANDLE;
+ stagingAllocations[i] = nullptr;
+ }
+}
+
+QVkTexture::~QVkTexture()
+{
+ release();
+}
+
+void QVkTexture::release()
+{
+ if (!image)
+ return;
+
+ QRhiVulkan::DeferredReleaseEntry e;
+ e.type = QRhiVulkan::DeferredReleaseEntry::Texture;
+ e.lastActiveFrameSlot = lastActiveFrameSlot;
+
+ e.texture.image = owns ? image : VK_NULL_HANDLE;
+ e.texture.imageView = imageView;
+ e.texture.allocation = owns ? imageAlloc : nullptr;
+
+ for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
+ e.texture.stagingBuffers[i] = stagingBuffers[i];
+ e.texture.stagingAllocations[i] = stagingAllocations[i];
+
+ stagingBuffers[i] = VK_NULL_HANDLE;
+ stagingAllocations[i] = nullptr;
+ }
+
+ image = VK_NULL_HANDLE;
+ imageView = VK_NULL_HANDLE;
+ imageAlloc = nullptr;
+ nativeHandlesStruct.image = VK_NULL_HANDLE;
+
+ QRHI_RES_RHI(QRhiVulkan);
+ rhiD->releaseQueue.append(e);
+
+ QRHI_PROF;
+ QRHI_PROF_F(releaseTexture(this));
+
+ rhiD->unregisterResource(this);
+}
+
+bool QVkTexture::prepareBuild(QSize *adjustedSize)
+{
+ if (image)
+ release();
+
+ QRHI_RES_RHI(QRhiVulkan);
+ vkformat = toVkTextureFormat(m_format, m_flags);
+ VkFormatProperties props;
+ rhiD->f->vkGetPhysicalDeviceFormatProperties(rhiD->physDev, vkformat, &props);
+ const bool canSampleOptimal = (props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT);
+ if (!canSampleOptimal) {
+ qWarning("Texture sampling with optimal tiling for format %d not supported", vkformat);
+ return false;
+ }
+
+ const QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize;
+ const bool isCube = m_flags.testFlag(CubeMap);
+ const bool hasMipMaps = m_flags.testFlag(MipMapped);
+
+ mipLevelCount = hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1;
+ const int maxLevels = QRhi::MAX_LEVELS;
+ if (mipLevelCount > maxLevels) {
+ qWarning("Too many mip levels (%d, max is %d), truncating mip chain", mipLevelCount, maxLevels);
+ mipLevelCount = maxLevels;
+ }
+ samples = rhiD->effectiveSampleCount(m_sampleCount);
+ if (samples > VK_SAMPLE_COUNT_1_BIT) {
+ if (isCube) {
+ qWarning("Cubemap texture cannot be multisample");
+ return false;
+ }
+ if (hasMipMaps) {
+ qWarning("Multisample texture cannot have mipmaps");
+ return false;
+ }
+ }
+
+ usageState.layout = VK_IMAGE_LAYOUT_PREINITIALIZED;
+ usageState.access = 0;
+ usageState.stage = 0;
+
+ if (adjustedSize)
+ *adjustedSize = size;
+
+ return true;
+}
+
+bool QVkTexture::finishBuild()
+{
+ QRHI_RES_RHI(QRhiVulkan);
+
+ const bool isDepth = isDepthTextureFormat(m_format);
+ const bool isCube = m_flags.testFlag(CubeMap);
+
+ VkImageViewCreateInfo viewInfo;
+ memset(&viewInfo, 0, sizeof(viewInfo));
+ viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
+ viewInfo.image = image;
+ viewInfo.viewType = isCube ? VK_IMAGE_VIEW_TYPE_CUBE : VK_IMAGE_VIEW_TYPE_2D;
+ viewInfo.format = vkformat;
+ viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
+ viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
+ viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
+ viewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
+ viewInfo.subresourceRange.aspectMask = isDepth ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT;
+ viewInfo.subresourceRange.levelCount = mipLevelCount;
+ viewInfo.subresourceRange.layerCount = isCube ? 6 : 1;
+
+ VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &imageView);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create image view: %d", err);
+ return false;
+ }
+
+ nativeHandlesStruct.image = image;
+
+ lastActiveFrameSlot = -1;
+ generation += 1;
+
+ return true;
+}
+
+bool QVkTexture::build()
+{
+ QSize size;
+ if (!prepareBuild(&size))
+ return false;
+
+ const bool isRenderTarget = m_flags.testFlag(QRhiTexture::RenderTarget);
+ const bool isDepth = isDepthTextureFormat(m_format);
+ const bool isCube = m_flags.testFlag(CubeMap);
+
+ VkImageCreateInfo imageInfo;
+ memset(&imageInfo, 0, sizeof(imageInfo));
+ imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
+ imageInfo.flags = isCube ? VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT : 0;
+ imageInfo.imageType = VK_IMAGE_TYPE_2D;
+ imageInfo.format = vkformat;
+ imageInfo.extent.width = size.width();
+ imageInfo.extent.height = size.height();
+ imageInfo.extent.depth = 1;
+ imageInfo.mipLevels = mipLevelCount;
+ imageInfo.arrayLayers = isCube ? 6 : 1;
+ imageInfo.samples = samples;
+ imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
+ imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
+
+ imageInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
+ if (isRenderTarget) {
+ if (isDepth)
+ imageInfo.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
+ else
+ imageInfo.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
+ }
+ if (m_flags.testFlag(QRhiTexture::UsedAsTransferSource))
+ imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
+ if (m_flags.testFlag(QRhiTexture::UsedWithGenerateMips))
+ imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
+
+ VmaAllocationCreateInfo allocInfo;
+ memset(&allocInfo, 0, sizeof(allocInfo));
+ allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
+
+ QRHI_RES_RHI(QRhiVulkan);
+ VmaAllocation allocation;
+ VkResult err = vmaCreateImage(toVmaAllocator(rhiD->allocator), &imageInfo, &allocInfo, &image, &allocation, nullptr);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create image: %d", err);
+ return false;
+ }
+ imageAlloc = allocation;
+
+ if (!finishBuild())
+ return false;
+
+ rhiD->setObjectName(uint64_t(image), VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT, m_objectName);
+
+ QRHI_PROF;
+ QRHI_PROF_F(newTexture(this, true, mipLevelCount, isCube ? 6 : 1, samples));
+
+ owns = true;
+ rhiD->registerResource(this);
+ return true;
+}
+
+bool QVkTexture::buildFrom(const QRhiNativeHandles *src)
+{
+ const QRhiVulkanTextureNativeHandles *h = static_cast<const QRhiVulkanTextureNativeHandles *>(src);
+ if (!h || !h->image)
+ return false;
+
+ if (!prepareBuild())
+ return false;
+
+ image = h->image;
+
+ if (!finishBuild())
+ return false;
+
+ QRHI_PROF;
+ QRHI_PROF_F(newTexture(this, false, mipLevelCount, m_flags.testFlag(CubeMap) ? 6 : 1, samples));
+
+ usageState.layout = h->layout;
+
+ owns = false;
+ QRHI_RES_RHI(QRhiVulkan);
+ rhiD->registerResource(this);
+ return true;
+}
+
+const QRhiNativeHandles *QVkTexture::nativeHandles()
+{
+ nativeHandlesStruct.layout = usageState.layout;
+ return &nativeHandlesStruct;
+}
+
+QVkSampler::QVkSampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode,
+ AddressMode u, AddressMode v)
+ : QRhiSampler(rhi, magFilter, minFilter, mipmapMode, u, v)
+{
+}
+
+QVkSampler::~QVkSampler()
+{
+ release();
+}
+
+void QVkSampler::release()
+{
+ if (!sampler)
+ return;
+
+ QRhiVulkan::DeferredReleaseEntry e;
+ e.type = QRhiVulkan::DeferredReleaseEntry::Sampler;
+ e.lastActiveFrameSlot = lastActiveFrameSlot;
+
+ e.sampler.sampler = sampler;
+ sampler = VK_NULL_HANDLE;
+
+ QRHI_RES_RHI(QRhiVulkan);
+ rhiD->releaseQueue.append(e);
+ rhiD->unregisterResource(this);
+}
+
+bool QVkSampler::build()
+{
+ if (sampler)
+ release();
+
+ VkSamplerCreateInfo samplerInfo;
+ memset(&samplerInfo, 0, sizeof(samplerInfo));
+ samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
+ samplerInfo.magFilter = toVkFilter(m_magFilter);
+ samplerInfo.minFilter = toVkFilter(m_minFilter);
+ samplerInfo.mipmapMode = toVkMipmapMode(m_mipmapMode);
+ samplerInfo.addressModeU = toVkAddressMode(m_addressU);
+ samplerInfo.addressModeV = toVkAddressMode(m_addressV);
+ samplerInfo.addressModeW = toVkAddressMode(m_addressW);
+ samplerInfo.maxAnisotropy = 1.0f;
+ samplerInfo.compareEnable = m_compareOp != Never;
+ samplerInfo.compareOp = toVkTextureCompareOp(m_compareOp);
+ samplerInfo.maxLod = m_mipmapMode == None ? 0.25f : 1000.0f;
+
+ QRHI_RES_RHI(QRhiVulkan);
+ VkResult err = rhiD->df->vkCreateSampler(rhiD->dev, &samplerInfo, nullptr, &sampler);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create sampler: %d", err);
+ return false;
+ }
+
+ lastActiveFrameSlot = -1;
+ generation += 1;
+ rhiD->registerResource(this);
+ return true;
+}
+
+QVkRenderPassDescriptor::QVkRenderPassDescriptor(QRhiImplementation *rhi)
+ : QRhiRenderPassDescriptor(rhi)
+{
+}
+
+QVkRenderPassDescriptor::~QVkRenderPassDescriptor()
+{
+ release();
+}
+
+void QVkRenderPassDescriptor::release()
+{
+ if (!rp)
+ return;
+
+ if (!ownsRp) {
+ rp = VK_NULL_HANDLE;
+ return;
+ }
+
+ QRhiVulkan::DeferredReleaseEntry e;
+ e.type = QRhiVulkan::DeferredReleaseEntry::RenderPass;
+ e.lastActiveFrameSlot = lastActiveFrameSlot;
+
+ e.renderPass.rp = rp;
+
+ rp = VK_NULL_HANDLE;
+
+ QRHI_RES_RHI(QRhiVulkan);
+ rhiD->releaseQueue.append(e);
+
+ rhiD->unregisterResource(this);
+}
+
+QVkReferenceRenderTarget::QVkReferenceRenderTarget(QRhiImplementation *rhi)
+ : QRhiRenderTarget(rhi)
+{
+}
+
+QVkReferenceRenderTarget::~QVkReferenceRenderTarget()
+{
+ release();
+}
+
+void QVkReferenceRenderTarget::release()
+{
+ // nothing to do here
+}
+
+QSize QVkReferenceRenderTarget::pixelSize() const
+{
+ return d.pixelSize;
+}
+
+float QVkReferenceRenderTarget::devicePixelRatio() const
+{
+ return d.dpr;
+}
+
+int QVkReferenceRenderTarget::sampleCount() const
+{
+ return d.sampleCount;
+}
+
+QVkTextureRenderTarget::QVkTextureRenderTarget(QRhiImplementation *rhi,
+ const QRhiTextureRenderTargetDescription &desc,
+ Flags flags)
+ : QRhiTextureRenderTarget(rhi, desc, flags)
+{
+ for (int att = 0; att < QVkRenderTargetData::MAX_COLOR_ATTACHMENTS; ++att) {
+ rtv[att] = VK_NULL_HANDLE;
+ resrtv[att] = VK_NULL_HANDLE;
+ }
+}
+
+QVkTextureRenderTarget::~QVkTextureRenderTarget()
+{
+ release();
+}
+
+void QVkTextureRenderTarget::release()
+{
+ if (!d.fb)
+ return;
+
+ QRhiVulkan::DeferredReleaseEntry e;
+ e.type = QRhiVulkan::DeferredReleaseEntry::TextureRenderTarget;
+ e.lastActiveFrameSlot = lastActiveFrameSlot;
+
+ e.textureRenderTarget.fb = d.fb;
+ d.fb = VK_NULL_HANDLE;
+
+ for (int att = 0; att < QVkRenderTargetData::MAX_COLOR_ATTACHMENTS; ++att) {
+ e.textureRenderTarget.rtv[att] = rtv[att];
+ e.textureRenderTarget.resrtv[att] = resrtv[att];
+ rtv[att] = VK_NULL_HANDLE;
+ resrtv[att] = VK_NULL_HANDLE;
+ }
+
+ QRHI_RES_RHI(QRhiVulkan);
+ rhiD->releaseQueue.append(e);
+
+ rhiD->unregisterResource(this);
+}
+
+QRhiRenderPassDescriptor *QVkTextureRenderTarget::newCompatibleRenderPassDescriptor()
+{
+ // not yet built so cannot rely on data computed in build()
+
+ QRHI_RES_RHI(QRhiVulkan);
+ QVkRenderPassDescriptor *rp = new QVkRenderPassDescriptor(m_rhi);
+ if (!rhiD->createOffscreenRenderPass(&rp->rp,
+ m_desc.colorAttachments(),
+ m_flags.testFlag(QRhiTextureRenderTarget::PreserveColorContents),
+ m_flags.testFlag(QRhiTextureRenderTarget::PreserveDepthStencilContents),
+ m_desc.depthStencilBuffer(),
+ m_desc.depthTexture()))
+ {
+ delete rp;
+ return nullptr;
+ }
+
+ rp->ownsRp = true;
+ rhiD->registerResource(rp);
+ return rp;
+}
+
+bool QVkTextureRenderTarget::build()
+{
+ if (d.fb)
+ release();
+
+ const QVector<QRhiColorAttachment> colorAttachments = m_desc.colorAttachments();
+ Q_ASSERT(!colorAttachments.isEmpty() || m_desc.depthTexture());
+ Q_ASSERT(!m_desc.depthStencilBuffer() || !m_desc.depthTexture());
+ const bool hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
+
+ QRHI_RES_RHI(QRhiVulkan);
+ QVarLengthArray<VkImageView, 8> views;
+
+ d.colorAttCount = colorAttachments.count();
+ for (int i = 0; i < d.colorAttCount; ++i) {
+ QVkTexture *texD = QRHI_RES(QVkTexture, colorAttachments[i].texture());
+ QVkRenderBuffer *rbD = QRHI_RES(QVkRenderBuffer, colorAttachments[i].renderBuffer());
+ Q_ASSERT(texD || rbD);
+ if (texD) {
+ Q_ASSERT(texD->flags().testFlag(QRhiTexture::RenderTarget));
+ VkImageViewCreateInfo viewInfo;
+ memset(&viewInfo, 0, sizeof(viewInfo));
+ viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
+ viewInfo.image = texD->image;
+ viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
+ viewInfo.format = texD->vkformat;
+ viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
+ viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
+ viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
+ viewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
+ viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ viewInfo.subresourceRange.baseMipLevel = colorAttachments[i].level();
+ viewInfo.subresourceRange.levelCount = 1;
+ viewInfo.subresourceRange.baseArrayLayer = colorAttachments[i].layer();
+ viewInfo.subresourceRange.layerCount = 1;
+ VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &rtv[i]);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create render target image view: %d", err);
+ return false;
+ }
+ views.append(rtv[i]);
+ if (i == 0) {
+ d.pixelSize = texD->pixelSize();
+ d.sampleCount = texD->samples;
+ }
+ } else if (rbD) {
+ Q_ASSERT(rbD->backingTexture);
+ views.append(rbD->backingTexture->imageView);
+ if (i == 0) {
+ d.pixelSize = rbD->pixelSize();
+ d.sampleCount = rbD->samples;
+ }
+ }
+ }
+ d.dpr = 1;
+
+ if (hasDepthStencil) {
+ if (m_desc.depthTexture()) {
+ QVkTexture *depthTexD = QRHI_RES(QVkTexture, m_desc.depthTexture());
+ views.append(depthTexD->imageView);
+ if (d.colorAttCount == 0) {
+ d.pixelSize = depthTexD->pixelSize();
+ d.sampleCount = depthTexD->samples;
+ }
+ } else {
+ QVkRenderBuffer *depthRbD = QRHI_RES(QVkRenderBuffer, m_desc.depthStencilBuffer());
+ views.append(depthRbD->imageView);
+ if (d.colorAttCount == 0) {
+ d.pixelSize = depthRbD->pixelSize();
+ d.sampleCount = depthRbD->samples;
+ }
+ }
+ d.dsAttCount = 1;
+ } else {
+ d.dsAttCount = 0;
+ }
+
+ d.resolveAttCount = 0;
+ for (int i = 0; i < d.colorAttCount; ++i) {
+ if (colorAttachments[i].resolveTexture()) {
+ QVkTexture *resTexD = QRHI_RES(QVkTexture, colorAttachments[i].resolveTexture());
+ Q_ASSERT(resTexD->flags().testFlag(QRhiTexture::RenderTarget));
+ d.resolveAttCount += 1;
+
+ VkImageViewCreateInfo viewInfo;
+ memset(&viewInfo, 0, sizeof(viewInfo));
+ viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
+ viewInfo.image = resTexD->image;
+ viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
+ viewInfo.format = resTexD->vkformat;
+ viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
+ viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
+ viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
+ viewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
+ viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ viewInfo.subresourceRange.baseMipLevel = colorAttachments[i].resolveLevel();
+ viewInfo.subresourceRange.levelCount = 1;
+ viewInfo.subresourceRange.baseArrayLayer = colorAttachments[i].resolveLayer();
+ viewInfo.subresourceRange.layerCount = 1;
+ VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &resrtv[i]);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create render target resolve image view: %d", err);
+ return false;
+ }
+ views.append(resrtv[i]);
+ }
+ }
+
+ if (!m_renderPassDesc)
+ qWarning("QVkTextureRenderTarget: No renderpass descriptor set. See newCompatibleRenderPassDescriptor() and setRenderPassDescriptor().");
+
+ d.rp = QRHI_RES(QVkRenderPassDescriptor, m_renderPassDesc);
+ Q_ASSERT(d.rp && d.rp->rp);
+
+ VkFramebufferCreateInfo fbInfo;
+ memset(&fbInfo, 0, sizeof(fbInfo));
+ fbInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
+ fbInfo.renderPass = d.rp->rp;
+ fbInfo.attachmentCount = d.colorAttCount + d.dsAttCount + d.resolveAttCount;
+ fbInfo.pAttachments = views.constData();
+ fbInfo.width = d.pixelSize.width();
+ fbInfo.height = d.pixelSize.height();
+ fbInfo.layers = 1;
+
+ VkResult err = rhiD->df->vkCreateFramebuffer(rhiD->dev, &fbInfo, nullptr, &d.fb);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create framebuffer: %d", err);
+ return false;
+ }
+
+ lastActiveFrameSlot = -1;
+ rhiD->registerResource(this);
+ return true;
+}
+
+QSize QVkTextureRenderTarget::pixelSize() const
+{
+ return d.pixelSize;
+}
+
+float QVkTextureRenderTarget::devicePixelRatio() const
+{
+ return d.dpr;
+}
+
+int QVkTextureRenderTarget::sampleCount() const
+{
+ return d.sampleCount;
+}
+
+QVkShaderResourceBindings::QVkShaderResourceBindings(QRhiImplementation *rhi)
+ : QRhiShaderResourceBindings(rhi)
+{
+}
+
+QVkShaderResourceBindings::~QVkShaderResourceBindings()
+{
+ release();
+}
+
+void QVkShaderResourceBindings::release()
+{
+ if (!layout)
+ return;
+
+ sortedBindings.clear();
+
+ QRhiVulkan::DeferredReleaseEntry e;
+ e.type = QRhiVulkan::DeferredReleaseEntry::ShaderResourceBindings;
+ e.lastActiveFrameSlot = lastActiveFrameSlot;
+
+ e.shaderResourceBindings.poolIndex = poolIndex;
+ e.shaderResourceBindings.layout = layout;
+
+ poolIndex = -1;
+ layout = VK_NULL_HANDLE;
+ for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i)
+ descSets[i] = VK_NULL_HANDLE;
+
+ QRHI_RES_RHI(QRhiVulkan);
+ rhiD->releaseQueue.append(e);
+
+ rhiD->unregisterResource(this);
+}
+
+bool QVkShaderResourceBindings::build()
+{
+ if (layout)
+ release();
+
+ for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i)
+ descSets[i] = VK_NULL_HANDLE;
+
+ sortedBindings = m_bindings;
+ std::sort(sortedBindings.begin(), sortedBindings.end(),
+ [](const QRhiShaderResourceBinding &a, const QRhiShaderResourceBinding &b)
+ {
+ return QRhiShaderResourceBindingPrivate::get(&a)->binding < QRhiShaderResourceBindingPrivate::get(&b)->binding;
+ });
+
+ QVarLengthArray<VkDescriptorSetLayoutBinding, 4> vkbindings;
+ for (const QRhiShaderResourceBinding &binding : qAsConst(sortedBindings)) {
+ const QRhiShaderResourceBindingPrivate *b = QRhiShaderResourceBindingPrivate::get(&binding);
+ VkDescriptorSetLayoutBinding vkbinding;
+ memset(&vkbinding, 0, sizeof(vkbinding));
+ vkbinding.binding = b->binding;
+ vkbinding.descriptorType = toVkDescriptorType(b);
+ vkbinding.descriptorCount = 1; // no array support yet
+ vkbinding.stageFlags = toVkShaderStageFlags(b->stage);
+ vkbindings.append(vkbinding);
+ }
+
+ VkDescriptorSetLayoutCreateInfo layoutInfo;
+ memset(&layoutInfo, 0, sizeof(layoutInfo));
+ layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
+ layoutInfo.bindingCount = uint32_t(vkbindings.count());
+ layoutInfo.pBindings = vkbindings.constData();
+
+ QRHI_RES_RHI(QRhiVulkan);
+ VkResult err = rhiD->df->vkCreateDescriptorSetLayout(rhiD->dev, &layoutInfo, nullptr, &layout);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create descriptor set layout: %d", err);
+ return false;
+ }
+
+ VkDescriptorSetAllocateInfo allocInfo;
+ memset(&allocInfo, 0, sizeof(allocInfo));
+ allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
+ allocInfo.descriptorSetCount = QVK_FRAMES_IN_FLIGHT;
+ VkDescriptorSetLayout layouts[QVK_FRAMES_IN_FLIGHT];
+ for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i)
+ layouts[i] = layout;
+ allocInfo.pSetLayouts = layouts;
+ if (!rhiD->allocateDescriptorSet(&allocInfo, descSets, &poolIndex))
+ return false;
+
+ rhiD->updateShaderResourceBindings(this);
+
+ lastActiveFrameSlot = -1;
+ generation += 1;
+ rhiD->registerResource(this);
+ return true;
+}
+
+QVkGraphicsPipeline::QVkGraphicsPipeline(QRhiImplementation *rhi)
+ : QRhiGraphicsPipeline(rhi)
+{
+}
+
+QVkGraphicsPipeline::~QVkGraphicsPipeline()
+{
+ release();
+}
+
+void QVkGraphicsPipeline::release()
+{
+ if (!pipeline && !layout)
+ return;
+
+ QRhiVulkan::DeferredReleaseEntry e;
+ e.type = QRhiVulkan::DeferredReleaseEntry::Pipeline;
+ e.lastActiveFrameSlot = lastActiveFrameSlot;
+
+ e.pipelineState.pipeline = pipeline;
+ e.pipelineState.layout = layout;
+
+ pipeline = VK_NULL_HANDLE;
+ layout = VK_NULL_HANDLE;
+
+ QRHI_RES_RHI(QRhiVulkan);
+ rhiD->releaseQueue.append(e);
+
+ rhiD->unregisterResource(this);
+}
+
+bool QVkGraphicsPipeline::build()
+{
+ if (pipeline)
+ release();
+
+ QRHI_RES_RHI(QRhiVulkan);
+ if (!rhiD->ensurePipelineCache())
+ return false;
+
+ VkPipelineLayoutCreateInfo pipelineLayoutInfo;
+ memset(&pipelineLayoutInfo, 0, sizeof(pipelineLayoutInfo));
+ pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
+ pipelineLayoutInfo.setLayoutCount = 1;
+ QVkShaderResourceBindings *srbD = QRHI_RES(QVkShaderResourceBindings, m_shaderResourceBindings);
+ Q_ASSERT(m_shaderResourceBindings && srbD->layout);
+ pipelineLayoutInfo.pSetLayouts = &srbD->layout;
+ VkResult err = rhiD->df->vkCreatePipelineLayout(rhiD->dev, &pipelineLayoutInfo, nullptr, &layout);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create pipeline layout: %d", err);
+ return false;
+ }
+
+ VkGraphicsPipelineCreateInfo pipelineInfo;
+ memset(&pipelineInfo, 0, sizeof(pipelineInfo));
+ pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
+
+ QVarLengthArray<VkShaderModule, 4> shaders;
+ QVarLengthArray<VkPipelineShaderStageCreateInfo, 4> shaderStageCreateInfos;
+ for (const QRhiGraphicsShaderStage &shaderStage : m_shaderStages) {
+ const QShader bakedShader = shaderStage.shader();
+ const QShaderCode spirv = bakedShader.shader({ QShader::SpirvShader, 100, shaderStage.shaderVariant() });
+ if (spirv.shader().isEmpty()) {
+ qWarning() << "No SPIR-V 1.0 shader code found in baked shader" << bakedShader;
+ return false;
+ }
+ VkShaderModule shader = rhiD->createShader(spirv.shader());
+ if (shader) {
+ shaders.append(shader);
+ VkPipelineShaderStageCreateInfo shaderInfo;
+ memset(&shaderInfo, 0, sizeof(shaderInfo));
+ shaderInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
+ shaderInfo.stage = toVkShaderStage(shaderStage.type());
+ shaderInfo.module = shader;
+ shaderInfo.pName = spirv.entryPoint().constData();
+ shaderStageCreateInfos.append(shaderInfo);
+ }
+ }
+ pipelineInfo.stageCount = shaderStageCreateInfos.count();
+ pipelineInfo.pStages = shaderStageCreateInfos.constData();
+
+ const QVector<QRhiVertexInputBinding> bindings = m_vertexInputLayout.bindings();
+ QVarLengthArray<VkVertexInputBindingDescription, 4> vertexBindings;
+ QVarLengthArray<VkVertexInputBindingDivisorDescriptionEXT> nonOneStepRates;
+ for (int i = 0, ie = bindings.count(); i != ie; ++i) {
+ const QRhiVertexInputBinding &binding(bindings[i]);
+ VkVertexInputBindingDescription bindingInfo = {
+ uint32_t(i),
+ binding.stride(),
+ binding.classification() == QRhiVertexInputBinding::PerVertex
+ ? VK_VERTEX_INPUT_RATE_VERTEX : VK_VERTEX_INPUT_RATE_INSTANCE
+ };
+ if (binding.classification() == QRhiVertexInputBinding::PerInstance
+ && binding.instanceStepRate() != 1)
+ {
+ if (rhiD->vertexAttribDivisorAvailable) {
+ nonOneStepRates.append({ uint32_t(i), uint32_t(binding.instanceStepRate()) });
+ } else {
+ qWarning("QRhiVulkan: Instance step rates other than 1 not supported without "
+ "VK_EXT_vertex_attribute_divisor on the device and "
+ "VK_KHR_get_physical_device_properties2 on the instance");
+ }
+ }
+ vertexBindings.append(bindingInfo);
+ }
+ const QVector<QRhiVertexInputAttribute> attributes = m_vertexInputLayout.attributes();
+ QVarLengthArray<VkVertexInputAttributeDescription, 4> vertexAttributes;
+ for (const QRhiVertexInputAttribute &attribute : attributes) {
+ VkVertexInputAttributeDescription attributeInfo = {
+ uint32_t(attribute.location()),
+ uint32_t(attribute.binding()),
+ toVkAttributeFormat(attribute.format()),
+ attribute.offset()
+ };
+ vertexAttributes.append(attributeInfo);
+ }
+ VkPipelineVertexInputStateCreateInfo vertexInputInfo;
+ memset(&vertexInputInfo, 0, sizeof(vertexInputInfo));
+ vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
+ vertexInputInfo.vertexBindingDescriptionCount = vertexBindings.count();
+ vertexInputInfo.pVertexBindingDescriptions = vertexBindings.constData();
+ vertexInputInfo.vertexAttributeDescriptionCount = vertexAttributes.count();
+ vertexInputInfo.pVertexAttributeDescriptions = vertexAttributes.constData();
+ VkPipelineVertexInputDivisorStateCreateInfoEXT divisorInfo;
+ if (!nonOneStepRates.isEmpty()) {
+ memset(&divisorInfo, 0, sizeof(divisorInfo));
+ divisorInfo.sType = VkStructureType(1000190001); // VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_DIVISOR_STATE_CREATE_INFO_EXT
+ divisorInfo.vertexBindingDivisorCount = nonOneStepRates.count();
+ divisorInfo.pVertexBindingDivisors = nonOneStepRates.constData();
+ vertexInputInfo.pNext = &divisorInfo;
+ }
+ pipelineInfo.pVertexInputState = &vertexInputInfo;
+
+ QVarLengthArray<VkDynamicState, 8> dynEnable;
+ dynEnable << VK_DYNAMIC_STATE_VIEWPORT;
+ dynEnable << VK_DYNAMIC_STATE_SCISSOR; // ignore UsesScissor - Vulkan requires a scissor for the viewport always
+ if (m_flags.testFlag(QRhiGraphicsPipeline::UsesBlendConstants))
+ dynEnable << VK_DYNAMIC_STATE_BLEND_CONSTANTS;
+ if (m_flags.testFlag(QRhiGraphicsPipeline::UsesStencilRef))
+ dynEnable << VK_DYNAMIC_STATE_STENCIL_REFERENCE;
+
+ VkPipelineDynamicStateCreateInfo dynamicInfo;
+ memset(&dynamicInfo, 0, sizeof(dynamicInfo));
+ dynamicInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
+ dynamicInfo.dynamicStateCount = dynEnable.count();
+ dynamicInfo.pDynamicStates = dynEnable.constData();
+ pipelineInfo.pDynamicState = &dynamicInfo;
+
+ VkPipelineViewportStateCreateInfo viewportInfo;
+ memset(&viewportInfo, 0, sizeof(viewportInfo));
+ viewportInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
+ viewportInfo.viewportCount = viewportInfo.scissorCount = 1;
+ pipelineInfo.pViewportState = &viewportInfo;
+
+ VkPipelineInputAssemblyStateCreateInfo inputAsmInfo;
+ memset(&inputAsmInfo, 0, sizeof(inputAsmInfo));
+ inputAsmInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
+ inputAsmInfo.topology = toVkTopology(m_topology);
+ inputAsmInfo.primitiveRestartEnable = (m_topology == TriangleStrip || m_topology == LineStrip);
+ pipelineInfo.pInputAssemblyState = &inputAsmInfo;
+
+ VkPipelineRasterizationStateCreateInfo rastInfo;
+ memset(&rastInfo, 0, sizeof(rastInfo));
+ rastInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
+ rastInfo.cullMode = toVkCullMode(m_cullMode);
+ rastInfo.frontFace = toVkFrontFace(m_frontFace);
+ rastInfo.lineWidth = 1.0f;
+ pipelineInfo.pRasterizationState = &rastInfo;
+
+ VkPipelineMultisampleStateCreateInfo msInfo;
+ memset(&msInfo, 0, sizeof(msInfo));
+ msInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
+ msInfo.rasterizationSamples = rhiD->effectiveSampleCount(m_sampleCount);
+ pipelineInfo.pMultisampleState = &msInfo;
+
+ VkPipelineDepthStencilStateCreateInfo dsInfo;
+ memset(&dsInfo, 0, sizeof(dsInfo));
+ dsInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
+ dsInfo.depthTestEnable = m_depthTest;
+ dsInfo.depthWriteEnable = m_depthWrite;
+ dsInfo.depthCompareOp = toVkCompareOp(m_depthOp);
+ dsInfo.stencilTestEnable = m_stencilTest;
+ if (m_stencilTest) {
+ fillVkStencilOpState(&dsInfo.front, m_stencilFront);
+ dsInfo.front.compareMask = m_stencilReadMask;
+ dsInfo.front.writeMask = m_stencilWriteMask;
+ fillVkStencilOpState(&dsInfo.back, m_stencilBack);
+ dsInfo.back.compareMask = m_stencilReadMask;
+ dsInfo.back.writeMask = m_stencilWriteMask;
+ }
+ pipelineInfo.pDepthStencilState = &dsInfo;
+
+ VkPipelineColorBlendStateCreateInfo blendInfo;
+ memset(&blendInfo, 0, sizeof(blendInfo));
+ blendInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
+ QVarLengthArray<VkPipelineColorBlendAttachmentState, 4> vktargetBlends;
+ for (const QRhiGraphicsPipeline::TargetBlend &b : qAsConst(m_targetBlends)) {
+ VkPipelineColorBlendAttachmentState blend;
+ memset(&blend, 0, sizeof(blend));
+ blend.blendEnable = b.enable;
+ blend.srcColorBlendFactor = toVkBlendFactor(b.srcColor);
+ blend.dstColorBlendFactor = toVkBlendFactor(b.dstColor);
+ blend.colorBlendOp = toVkBlendOp(b.opColor);
+ blend.srcAlphaBlendFactor = toVkBlendFactor(b.srcAlpha);
+ blend.dstAlphaBlendFactor = toVkBlendFactor(b.dstAlpha);
+ blend.alphaBlendOp = toVkBlendOp(b.opAlpha);
+ blend.colorWriteMask = toVkColorComponents(b.colorWrite);
+ vktargetBlends.append(blend);
+ }
+ if (vktargetBlends.isEmpty()) {
+ VkPipelineColorBlendAttachmentState blend;
+ memset(&blend, 0, sizeof(blend));
+ blend.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT
+ | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
+ vktargetBlends.append(blend);
+ }
+ blendInfo.attachmentCount = vktargetBlends.count();
+ blendInfo.pAttachments = vktargetBlends.constData();
+ pipelineInfo.pColorBlendState = &blendInfo;
+
+ pipelineInfo.layout = layout;
+
+ Q_ASSERT(m_renderPassDesc && QRHI_RES(const QVkRenderPassDescriptor, m_renderPassDesc)->rp);
+ pipelineInfo.renderPass = QRHI_RES(const QVkRenderPassDescriptor, m_renderPassDesc)->rp;
+
+ err = rhiD->df->vkCreateGraphicsPipelines(rhiD->dev, rhiD->pipelineCache, 1, &pipelineInfo, nullptr, &pipeline);
+
+ for (VkShaderModule shader : shaders)
+ rhiD->df->vkDestroyShaderModule(rhiD->dev, shader, nullptr);
+
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create graphics pipeline: %d", err);
+ return false;
+ }
+
+ lastActiveFrameSlot = -1;
+ generation += 1;
+ rhiD->registerResource(this);
+ return true;
+}
+
+QVkCommandBuffer::QVkCommandBuffer(QRhiImplementation *rhi)
+ : QRhiCommandBuffer(rhi)
+{
+ resetState();
+}
+
+QVkCommandBuffer::~QVkCommandBuffer()
+{
+ release();
+}
+
+void QVkCommandBuffer::release()
+{
+ // nothing to do here, cb is not owned by us
+}
+
+QVkSwapChain::QVkSwapChain(QRhiImplementation *rhi)
+ : QRhiSwapChain(rhi),
+ rtWrapper(rhi),
+ cbWrapper(rhi)
+{
+}
+
+QVkSwapChain::~QVkSwapChain()
+{
+ release();
+}
+
+void QVkSwapChain::release()
+{
+ if (sc == VK_NULL_HANDLE)
+ return;
+
+ QRHI_RES_RHI(QRhiVulkan);
+ rhiD->swapchains.remove(this);
+ rhiD->releaseSwapChainResources(this);
+ surface = lastConnectedSurface = VK_NULL_HANDLE;
+
+ QRHI_PROF;
+ QRHI_PROF_F(releaseSwapChain(this));
+
+ rhiD->unregisterResource(this);
+}
+
+QRhiCommandBuffer *QVkSwapChain::currentFrameCommandBuffer()
+{
+ return &cbWrapper;
+}
+
+QRhiRenderTarget *QVkSwapChain::currentFrameRenderTarget()
+{
+ return &rtWrapper;
+}
+
+QSize QVkSwapChain::surfacePixelSize()
+{
+ if (!ensureSurface())
+ return QSize();
+
+ // The size from the QWindow may not exactly match the surface... so if a
+ // size is reported from the surface, use that.
+ VkSurfaceCapabilitiesKHR surfaceCaps;
+ memset(&surfaceCaps, 0, sizeof(surfaceCaps));
+ QRHI_RES_RHI(QRhiVulkan);
+ rhiD->vkGetPhysicalDeviceSurfaceCapabilitiesKHR(rhiD->physDev, surface, &surfaceCaps);
+ VkExtent2D bufferSize = surfaceCaps.currentExtent;
+ if (bufferSize.width == quint32(-1)) {
+ Q_ASSERT(bufferSize.height == quint32(-1));
+ return m_window->size() * m_window->devicePixelRatio();
+ }
+ return QSize(bufferSize.width, bufferSize.height);
+}
+
+QRhiRenderPassDescriptor *QVkSwapChain::newCompatibleRenderPassDescriptor()
+{
+ // not yet built so cannot rely on data computed in buildOrResize()
+
+ if (!ensureSurface()) // make sure sampleCount and colorFormat reflect what was requested
+ return nullptr;
+
+ QRHI_RES_RHI(QRhiVulkan);
+ QVkRenderPassDescriptor *rp = new QVkRenderPassDescriptor(m_rhi);
+ if (!rhiD->createDefaultRenderPass(&rp->rp,
+ m_depthStencil != nullptr,
+ samples,
+ colorFormat))
+ {
+ delete rp;
+ return nullptr;
+ }
+
+ rp->ownsRp = true;
+ rhiD->registerResource(rp);
+ return rp;
+}
+
+static inline bool isSrgbFormat(VkFormat format)
+{
+ switch (format) {
+ case VK_FORMAT_R8_SRGB:
+ Q_FALLTHROUGH();
+ case VK_FORMAT_R8G8_SRGB:
+ Q_FALLTHROUGH();
+ case VK_FORMAT_R8G8B8_SRGB:
+ Q_FALLTHROUGH();
+ case VK_FORMAT_B8G8R8_SRGB:
+ Q_FALLTHROUGH();
+ case VK_FORMAT_R8G8B8A8_SRGB:
+ Q_FALLTHROUGH();
+ case VK_FORMAT_B8G8R8A8_SRGB:
+ Q_FALLTHROUGH();
+ case VK_FORMAT_A8B8G8R8_SRGB_PACK32:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool QVkSwapChain::ensureSurface()
+{
+ // Do nothing when already done, however window may change so check the
+ // surface is still the same. Some of the queries below are very expensive
+ // with some implementations so it is important to do the rest only once
+ // per surface.
+
+ Q_ASSERT(m_window);
+ VkSurfaceKHR surf = QVulkanInstance::surfaceForWindow(m_window);
+ if (!surf) {
+ qWarning("Failed to get surface for window");
+ return false;
+ }
+ if (surface == surf)
+ return true;
+
+ surface = surf;
+
+ QRHI_RES_RHI(QRhiVulkan);
+ if (rhiD->gfxQueueFamilyIdx != -1) {
+ if (!rhiD->inst->supportsPresent(rhiD->physDev, rhiD->gfxQueueFamilyIdx, m_window)) {
+ qWarning("Presenting not supported on this window");
+ return false;
+ }
+ }
+
+ if (!rhiD->vkGetPhysicalDeviceSurfaceCapabilitiesKHR) {
+ rhiD->vkGetPhysicalDeviceSurfaceCapabilitiesKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR>(
+ rhiD->inst->getInstanceProcAddr("vkGetPhysicalDeviceSurfaceCapabilitiesKHR"));
+ rhiD->vkGetPhysicalDeviceSurfaceFormatsKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceFormatsKHR>(
+ rhiD->inst->getInstanceProcAddr("vkGetPhysicalDeviceSurfaceFormatsKHR"));
+ rhiD->vkGetPhysicalDeviceSurfacePresentModesKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfacePresentModesKHR>(
+ rhiD->inst->getInstanceProcAddr("vkGetPhysicalDeviceSurfacePresentModesKHR"));
+ if (!rhiD->vkGetPhysicalDeviceSurfaceCapabilitiesKHR
+ || !rhiD->vkGetPhysicalDeviceSurfaceFormatsKHR
+ || !rhiD->vkGetPhysicalDeviceSurfacePresentModesKHR)
+ {
+ qWarning("Physical device surface queries not available");
+ return false;
+ }
+ }
+
+ quint32 formatCount = 0;
+ rhiD->vkGetPhysicalDeviceSurfaceFormatsKHR(rhiD->physDev, surface, &formatCount, nullptr);
+ QVector<VkSurfaceFormatKHR> formats(formatCount);
+ if (formatCount)
+ rhiD->vkGetPhysicalDeviceSurfaceFormatsKHR(rhiD->physDev, surface, &formatCount, formats.data());
+
+ const bool srgbRequested = m_flags.testFlag(sRGB);
+ for (quint32 i = 0; i < formatCount; ++i) {
+ if (formats[i].format != VK_FORMAT_UNDEFINED && srgbRequested == isSrgbFormat(formats[i].format)) {
+ colorFormat = formats[i].format;
+ colorSpace = formats[i].colorSpace;
+ break;
+ }
+ }
+
+ samples = rhiD->effectiveSampleCount(m_sampleCount);
+
+ quint32 presModeCount = 0;
+ rhiD->vkGetPhysicalDeviceSurfacePresentModesKHR(rhiD->physDev, surface, &presModeCount, nullptr);
+ QVector<VkPresentModeKHR> presModes(presModeCount);
+ rhiD->vkGetPhysicalDeviceSurfacePresentModesKHR(rhiD->physDev, surface, &presModeCount, presModes.data());
+ supportedPresentationModes = presModes;
+
+ return true;
+}
+
+bool QVkSwapChain::buildOrResize()
+{
+ QRHI_RES_RHI(QRhiVulkan);
+ const bool needsRegistration = !window || window != m_window;
+
+ // Can be called multiple times due to window resizes - that is not the
+ // same as a simple release+build (as with other resources). Thus no
+ // release() here. See recreateSwapChain().
+
+ // except if the window actually changes
+ if (window && window != m_window)
+ release();
+
+ window = m_window;
+ m_currentPixelSize = surfacePixelSize();
+ pixelSize = m_currentPixelSize;
+
+ if (!rhiD->recreateSwapChain(this)) {
+ qWarning("Failed to create new swapchain");
+ return false;
+ }
+
+ if (needsRegistration)
+ rhiD->swapchains.insert(this);
+
+ if (m_depthStencil && m_depthStencil->sampleCount() != m_sampleCount) {
+ qWarning("Depth-stencil buffer's sampleCount (%d) does not match color buffers' sample count (%d). Expect problems.",
+ m_depthStencil->sampleCount(), m_sampleCount);
+ }
+ if (m_depthStencil && m_depthStencil->pixelSize() != pixelSize) {
+ qWarning("Depth-stencil buffer's size (%dx%d) does not match the surface size (%dx%d). Expect problems.",
+ m_depthStencil->pixelSize().width(), m_depthStencil->pixelSize().height(),
+ pixelSize.width(), pixelSize.height());
+ }
+
+ if (!m_renderPassDesc)
+ qWarning("QVkSwapChain: No renderpass descriptor set. See newCompatibleRenderPassDescriptor() and setRenderPassDescriptor().");
+
+ rtWrapper.d.rp = QRHI_RES(QVkRenderPassDescriptor, m_renderPassDesc);
+ Q_ASSERT(rtWrapper.d.rp && rtWrapper.d.rp->rp);
+
+ rtWrapper.d.pixelSize = pixelSize;
+ rtWrapper.d.dpr = window->devicePixelRatio();
+ rtWrapper.d.sampleCount = samples;
+ rtWrapper.d.colorAttCount = 1;
+ if (m_depthStencil) {
+ rtWrapper.d.dsAttCount = 1;
+ ds = QRHI_RES(QVkRenderBuffer, m_depthStencil);
+ } else {
+ rtWrapper.d.dsAttCount = 0;
+ ds = nullptr;
+ }
+ if (samples > VK_SAMPLE_COUNT_1_BIT)
+ rtWrapper.d.resolveAttCount = 1;
+ else
+ rtWrapper.d.resolveAttCount = 0;
+
+ for (int i = 0; i < bufferCount; ++i) {
+ QVkSwapChain::ImageResources &image(imageRes[i]);
+ VkImageView views[3] = { // color, ds, resolve
+ samples > VK_SAMPLE_COUNT_1_BIT ? image.msaaImageView : image.imageView,
+ ds ? ds->imageView : VK_NULL_HANDLE,
+ samples > VK_SAMPLE_COUNT_1_BIT ? image.imageView : VK_NULL_HANDLE
+ };
+
+ VkFramebufferCreateInfo fbInfo;
+ memset(&fbInfo, 0, sizeof(fbInfo));
+ fbInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
+ fbInfo.renderPass = rtWrapper.d.rp->rp;
+ fbInfo.attachmentCount = rtWrapper.d.colorAttCount + rtWrapper.d.dsAttCount + rtWrapper.d.resolveAttCount;
+ fbInfo.pAttachments = views;
+ fbInfo.width = pixelSize.width();
+ fbInfo.height = pixelSize.height();
+ fbInfo.layers = 1;
+
+ VkResult err = rhiD->df->vkCreateFramebuffer(rhiD->dev, &fbInfo, nullptr, &image.fb);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create framebuffer: %d", err);
+ return false;
+ }
+ }
+
+ frameCount = 0;
+
+ QRHI_PROF;
+ QRHI_PROF_F(resizeSwapChain(this, QVK_FRAMES_IN_FLIGHT, samples > VK_SAMPLE_COUNT_1_BIT ? QVK_FRAMES_IN_FLIGHT : 0, samples));
+
+ if (needsRegistration)
+ rhiD->registerResource(this);
+
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/rhi/qrhivulkan_p.h b/src/gui/rhi/qrhivulkan_p.h
new file mode 100644
index 0000000000..545ef5ad72
--- /dev/null
+++ b/src/gui/rhi/qrhivulkan_p.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gui module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QRHIVULKAN_H
+#define QRHIVULKAN_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qrhi_p.h>
+#include <QtGui/qvulkaninstance.h> // this is where vulkan.h gets pulled in
+
+QT_BEGIN_NAMESPACE
+
+struct Q_GUI_EXPORT QRhiVulkanInitParams : public QRhiInitParams
+{
+ QVulkanInstance *inst = nullptr;
+ QWindow *window = nullptr;
+};
+
+struct Q_GUI_EXPORT QRhiVulkanNativeHandles : public QRhiNativeHandles
+{
+ VkPhysicalDevice physDev = VK_NULL_HANDLE;
+ VkDevice dev = VK_NULL_HANDLE;
+ int gfxQueueFamilyIdx = -1;
+ VkQueue gfxQueue = VK_NULL_HANDLE;
+ VkCommandPool cmdPool = VK_NULL_HANDLE;
+ void *vmemAllocator = nullptr;
+};
+
+struct Q_GUI_EXPORT QRhiVulkanTextureNativeHandles : public QRhiNativeHandles
+{
+ VkImage image = VK_NULL_HANDLE;
+ VkImageLayout layout = VK_IMAGE_LAYOUT_GENERAL;
+};
+
+struct Q_GUI_EXPORT QRhiVulkanCommandBufferNativeHandles : public QRhiNativeHandles
+{
+ VkCommandBuffer commandBuffer = VK_NULL_HANDLE;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/rhi/qrhivulkan_p_p.h b/src/gui/rhi/qrhivulkan_p_p.h
new file mode 100644
index 0000000000..01acc40d58
--- /dev/null
+++ b/src/gui/rhi/qrhivulkan_p_p.h
@@ -0,0 +1,859 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gui module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QRHIVULKAN_P_H
+#define QRHIVULKAN_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qrhivulkan_p.h"
+#include "qrhi_p_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QVulkanFunctions;
+class QVulkanDeviceFunctions;
+
+static const int QVK_FRAMES_IN_FLIGHT = 2;
+
+static const int QVK_DESC_SETS_PER_POOL = 128;
+static const int QVK_UNIFORM_BUFFERS_PER_POOL = 256;
+static const int QVK_COMBINED_IMAGE_SAMPLERS_PER_POOL = 256;
+
+static const int QVK_MAX_ACTIVE_TIMESTAMP_PAIRS = 16;
+
+// no vk_mem_alloc.h available here, void* is good enough
+typedef void * QVkAlloc;
+typedef void * QVkAllocator;
+
+struct QVkBuffer : public QRhiBuffer
+{
+ QVkBuffer(QRhiImplementation *rhi, Type type, UsageFlags usage, int size);
+ ~QVkBuffer();
+ void release() override;
+ bool build() override;
+
+ VkBuffer buffers[QVK_FRAMES_IN_FLIGHT];
+ QVkAlloc allocations[QVK_FRAMES_IN_FLIGHT];
+ QVector<QRhiResourceUpdateBatchPrivate::DynamicBufferUpdate> pendingDynamicUpdates[QVK_FRAMES_IN_FLIGHT];
+ VkBuffer stagingBuffers[QVK_FRAMES_IN_FLIGHT];
+ QVkAlloc stagingAllocations[QVK_FRAMES_IN_FLIGHT];
+ struct UsageState {
+ VkAccessFlags access = 0;
+ VkPipelineStageFlags stage = 0;
+ };
+ UsageState usageState[QVK_FRAMES_IN_FLIGHT];
+ int lastActiveFrameSlot = -1;
+ uint generation = 0;
+ friend class QRhiVulkan;
+};
+
+struct QVkTexture;
+
+struct QVkRenderBuffer : public QRhiRenderBuffer
+{
+ QVkRenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize,
+ int sampleCount, Flags flags);
+ ~QVkRenderBuffer();
+ void release() override;
+ bool build() override;
+ QRhiTexture::Format backingFormat() const override;
+
+ VkDeviceMemory memory = VK_NULL_HANDLE;
+ VkImage image = VK_NULL_HANDLE;
+ VkImageView imageView = VK_NULL_HANDLE;
+ VkSampleCountFlagBits samples;
+ QVkTexture *backingTexture = nullptr;
+ VkFormat vkformat;
+ int lastActiveFrameSlot = -1;
+ friend class QRhiVulkan;
+};
+
+struct QVkTexture : public QRhiTexture
+{
+ QVkTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize,
+ int sampleCount, Flags flags);
+ ~QVkTexture();
+ void release() override;
+ bool build() override;
+ bool buildFrom(const QRhiNativeHandles *src) override;
+ const QRhiNativeHandles *nativeHandles() override;
+
+ bool prepareBuild(QSize *adjustedSize = nullptr);
+ bool finishBuild();
+
+ VkImage image = VK_NULL_HANDLE;
+ VkImageView imageView = VK_NULL_HANDLE;
+ QVkAlloc imageAlloc = nullptr;
+ VkBuffer stagingBuffers[QVK_FRAMES_IN_FLIGHT];
+ QVkAlloc stagingAllocations[QVK_FRAMES_IN_FLIGHT];
+ bool owns = true;
+ QRhiVulkanTextureNativeHandles nativeHandlesStruct;
+ struct UsageState {
+ // no tracking of subresource layouts (some operations can keep
+ // subresources in different layouts for some time, but that does not
+ // need to be kept track of)
+ VkImageLayout layout;
+ VkAccessFlags access;
+ VkPipelineStageFlags stage;
+ };
+ UsageState usageState;
+ VkFormat vkformat;
+ uint mipLevelCount = 0;
+ VkSampleCountFlagBits samples;
+ int lastActiveFrameSlot = -1;
+ uint generation = 0;
+ friend class QRhiVulkan;
+};
+
+struct QVkSampler : public QRhiSampler
+{
+ QVkSampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode,
+ AddressMode u, AddressMode v);
+ ~QVkSampler();
+ void release() override;
+ bool build() override;
+
+ VkSampler sampler = VK_NULL_HANDLE;
+ int lastActiveFrameSlot = -1;
+ uint generation = 0;
+ friend class QRhiVulkan;
+};
+
+struct QVkRenderPassDescriptor : public QRhiRenderPassDescriptor
+{
+ QVkRenderPassDescriptor(QRhiImplementation *rhi);
+ ~QVkRenderPassDescriptor();
+ void release() override;
+
+ VkRenderPass rp = VK_NULL_HANDLE;
+ bool ownsRp = false;
+ int lastActiveFrameSlot = -1;
+};
+
+struct QVkRenderTargetData
+{
+ VkFramebuffer fb = VK_NULL_HANDLE;
+ QVkRenderPassDescriptor *rp = nullptr;
+ QSize pixelSize;
+ float dpr = 1;
+ int sampleCount = 1;
+ int colorAttCount = 0;
+ int dsAttCount = 0;
+ int resolveAttCount = 0;
+ static const int MAX_COLOR_ATTACHMENTS = 8;
+};
+
+struct QVkReferenceRenderTarget : public QRhiRenderTarget
+{
+ QVkReferenceRenderTarget(QRhiImplementation *rhi);
+ ~QVkReferenceRenderTarget();
+ void release() override;
+
+ QSize pixelSize() const override;
+ float devicePixelRatio() const override;
+ int sampleCount() const override;
+
+ QVkRenderTargetData d;
+};
+
+struct QVkTextureRenderTarget : public QRhiTextureRenderTarget
+{
+ QVkTextureRenderTarget(QRhiImplementation *rhi, const QRhiTextureRenderTargetDescription &desc, Flags flags);
+ ~QVkTextureRenderTarget();
+ void release() override;
+
+ QSize pixelSize() const override;
+ float devicePixelRatio() const override;
+ int sampleCount() const override;
+
+ QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override;
+ bool build() override;
+
+ QVkRenderTargetData d;
+ VkImageView rtv[QVkRenderTargetData::MAX_COLOR_ATTACHMENTS];
+ VkImageView resrtv[QVkRenderTargetData::MAX_COLOR_ATTACHMENTS];
+ int lastActiveFrameSlot = -1;
+ friend class QRhiVulkan;
+};
+
+struct QVkShaderResourceBindings : public QRhiShaderResourceBindings
+{
+ QVkShaderResourceBindings(QRhiImplementation *rhi);
+ ~QVkShaderResourceBindings();
+ void release() override;
+ bool build() override;
+
+ QVector<QRhiShaderResourceBinding> sortedBindings;
+ int poolIndex = -1;
+ VkDescriptorSetLayout layout = VK_NULL_HANDLE;
+ VkDescriptorSet descSets[QVK_FRAMES_IN_FLIGHT]; // multiple sets to support dynamic buffers
+ int lastActiveFrameSlot = -1;
+ uint generation = 0;
+
+ // Keep track of the generation number of each referenced QRhi* to be able
+ // to detect that the underlying descriptor set became out of date and they
+ // need to be written again with the up-to-date VkBuffer etc. objects.
+ struct BoundUniformBufferData {
+ quint64 id;
+ uint generation;
+ };
+ struct BoundSampledTextureData {
+ quint64 texId;
+ uint texGeneration;
+ quint64 samplerId;
+ uint samplerGeneration;
+ };
+ struct BoundResourceData {
+ union {
+ BoundUniformBufferData ubuf;
+ BoundSampledTextureData stex;
+ };
+ };
+ QVector<BoundResourceData> boundResourceData[QVK_FRAMES_IN_FLIGHT];
+
+ friend class QRhiVulkan;
+};
+
+Q_DECLARE_TYPEINFO(QVkShaderResourceBindings::BoundResourceData, Q_MOVABLE_TYPE);
+
+struct QVkGraphicsPipeline : public QRhiGraphicsPipeline
+{
+ QVkGraphicsPipeline(QRhiImplementation *rhi);
+ ~QVkGraphicsPipeline();
+ void release() override;
+ bool build() override;
+
+ VkPipelineLayout layout = VK_NULL_HANDLE;
+ VkPipeline pipeline = VK_NULL_HANDLE;
+ int lastActiveFrameSlot = -1;
+ uint generation = 0;
+ friend class QRhiVulkan;
+};
+
+struct QVkCommandBuffer : public QRhiCommandBuffer
+{
+ QVkCommandBuffer(QRhiImplementation *rhi);
+ ~QVkCommandBuffer();
+ void release() override;
+
+ VkCommandBuffer cb = VK_NULL_HANDLE;
+ QRhiVulkanCommandBufferNativeHandles nativeHandlesStruct;
+
+ const QRhiNativeHandles *nativeHandles() {
+ nativeHandlesStruct.commandBuffer = cb;
+ return &nativeHandlesStruct;
+ }
+
+ void resetState() {
+ resetCommands();
+ currentTarget = nullptr;
+ resetCachedState();
+ }
+
+ void resetCachedState() {
+ currentPipeline = nullptr;
+ currentPipelineGeneration = 0;
+ currentSrb = nullptr;
+ currentSrbGeneration = 0;
+ currentDescSetSlot = -1;
+ currentIndexBuffer = VK_NULL_HANDLE;
+ currentIndexOffset = 0;
+ currentIndexFormat = VK_INDEX_TYPE_UINT16;
+ memset(currentVertexBuffers, 0, sizeof(currentVertexBuffers));
+ memset(currentVertexOffsets, 0, sizeof(currentVertexOffsets));
+ }
+
+ QRhiRenderTarget *currentTarget;
+ QRhiGraphicsPipeline *currentPipeline;
+ uint currentPipelineGeneration;
+ QRhiShaderResourceBindings *currentSrb;
+ uint currentSrbGeneration;
+ int currentDescSetSlot;
+ VkBuffer currentIndexBuffer;
+ quint32 currentIndexOffset;
+ VkIndexType currentIndexFormat;
+ static const int VERTEX_INPUT_RESOURCE_SLOT_COUNT = 32;
+ VkBuffer currentVertexBuffers[VERTEX_INPUT_RESOURCE_SLOT_COUNT];
+ quint32 currentVertexOffsets[VERTEX_INPUT_RESOURCE_SLOT_COUNT];
+
+ struct Command {
+ enum Cmd {
+ CopyBuffer,
+ CopyBufferToImage,
+ CopyImage,
+ CopyImageToBuffer,
+ ImageBarrier,
+ BufferBarrier,
+ BlitImage,
+ BeginRenderPass,
+ EndRenderPass,
+ BindPipeline,
+ BindDescriptorSet,
+ BindVertexBuffer,
+ BindIndexBuffer,
+ SetViewport,
+ SetScissor,
+ SetBlendConstants,
+ SetStencilRef,
+ Draw,
+ DrawIndexed,
+ DebugMarkerBegin,
+ DebugMarkerEnd,
+ DebugMarkerInsert,
+ TransitionPassResources
+ };
+ Cmd cmd;
+
+ union Args {
+ struct {
+ VkBuffer src;
+ VkBuffer dst;
+ VkBufferCopy desc;
+ } copyBuffer;
+ struct {
+ VkBuffer src;
+ VkImage dst;
+ VkImageLayout dstLayout;
+ int count;
+ int bufferImageCopyIndex;
+ } copyBufferToImage;
+ struct {
+ VkImage src;
+ VkImageLayout srcLayout;
+ VkImage dst;
+ VkImageLayout dstLayout;
+ VkImageCopy desc;
+ } copyImage;
+ struct {
+ VkImage src;
+ VkImageLayout srcLayout;
+ VkBuffer dst;
+ VkBufferImageCopy desc;
+ } copyImageToBuffer;
+ struct {
+ VkPipelineStageFlags srcStageMask;
+ VkPipelineStageFlags dstStageMask;
+ VkImageMemoryBarrier desc;
+ } imageBarrier;
+ struct {
+ VkPipelineStageFlags srcStageMask;
+ VkPipelineStageFlags dstStageMask;
+ VkBufferMemoryBarrier desc;
+ } bufferBarrier;
+ struct {
+ VkImage src;
+ VkImageLayout srcLayout;
+ VkImage dst;
+ VkImageLayout dstLayout;
+ VkFilter filter;
+ VkImageBlit desc;
+ } blitImage;
+ struct {
+ VkRenderPassBeginInfo desc;
+ int clearValueIndex;
+ } beginRenderPass;
+ struct {
+ } endRenderPass;
+ struct {
+ VkPipelineBindPoint bindPoint;
+ VkPipeline pipeline;
+ } bindPipeline;
+ struct {
+ VkPipelineBindPoint bindPoint;
+ VkPipelineLayout pipelineLayout;
+ VkDescriptorSet descSet;
+ int dynamicOffsetCount;
+ int dynamicOffsetIndex;
+ } bindDescriptorSet;
+ struct {
+ int startBinding;
+ int count;
+ int vertexBufferIndex;
+ int vertexBufferOffsetIndex;
+ } bindVertexBuffer;
+ struct {
+ VkBuffer buf;
+ VkDeviceSize ofs;
+ VkIndexType type;
+ } bindIndexBuffer;
+ struct {
+ VkViewport viewport;
+ } setViewport;
+ struct {
+ VkRect2D scissor;
+ } setScissor;
+ struct {
+ float c[4];
+ } setBlendConstants;
+ struct {
+ uint32_t ref;
+ } setStencilRef;
+ struct {
+ uint32_t vertexCount;
+ uint32_t instanceCount;
+ uint32_t firstVertex;
+ uint32_t firstInstance;
+ } draw;
+ struct {
+ uint32_t indexCount;
+ uint32_t instanceCount;
+ uint32_t firstIndex;
+ int32_t vertexOffset;
+ uint32_t firstInstance;
+ } drawIndexed;
+ struct {
+ VkDebugMarkerMarkerInfoEXT marker;
+ int markerNameIndex;
+ } debugMarkerBegin;
+ struct {
+ } debugMarkerEnd;
+ struct {
+ VkDebugMarkerMarkerInfoEXT marker;
+ } debugMarkerInsert;
+ struct {
+ int trackerIndex;
+ } transitionResources;
+ } args;
+ };
+ QVector<Command> commands;
+ QVarLengthArray<QRhiPassResourceTracker, 8> passResTrackers;
+ int currentPassResTrackerIndex;
+
+ void resetCommands() {
+ commands.clear();
+ passResTrackers.clear();
+ currentPassResTrackerIndex = -1;
+ resetPools();
+ }
+
+ void resetPools() {
+ pools.clearValue.clear();
+ pools.bufferImageCopy.clear();
+ pools.dynamicOffset.clear();
+ pools.vertexBuffer.clear();
+ pools.vertexBufferOffset.clear();
+ pools.debugMarkerName.clear();
+ }
+
+ struct {
+ QVarLengthArray<VkClearValue, 4> clearValue;
+ QVarLengthArray<VkBufferImageCopy, 16> bufferImageCopy;
+ QVarLengthArray<uint32_t, 4> dynamicOffset;
+ QVarLengthArray<VkBuffer, 4> vertexBuffer;
+ QVarLengthArray<VkDeviceSize, 4> vertexBufferOffset;
+ QVarLengthArray<QByteArray, 4> debugMarkerName;
+ } pools;
+
+ friend class QRhiVulkan;
+};
+
+Q_DECLARE_TYPEINFO(QVkCommandBuffer::Command, Q_MOVABLE_TYPE);
+
+struct QVkSwapChain : public QRhiSwapChain
+{
+ QVkSwapChain(QRhiImplementation *rhi);
+ ~QVkSwapChain();
+ void release() override;
+
+ QRhiCommandBuffer *currentFrameCommandBuffer() override;
+ QRhiRenderTarget *currentFrameRenderTarget() override;
+
+ QSize surfacePixelSize() override;
+
+ QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override;
+ bool buildOrResize() override;
+
+ bool ensureSurface();
+
+ static const quint32 MAX_BUFFER_COUNT = 3;
+
+ QWindow *window = nullptr;
+ QSize pixelSize;
+ bool supportsReadback = false;
+ VkSwapchainKHR sc = VK_NULL_HANDLE;
+ int bufferCount = 0;
+ VkSurfaceKHR surface = VK_NULL_HANDLE;
+ VkSurfaceKHR lastConnectedSurface = VK_NULL_HANDLE;
+ VkFormat colorFormat = VK_FORMAT_B8G8R8A8_UNORM;
+ VkColorSpaceKHR colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
+ QVkRenderBuffer *ds = nullptr;
+ VkSampleCountFlagBits samples = VK_SAMPLE_COUNT_1_BIT;
+ QVector<VkPresentModeKHR> supportedPresentationModes;
+ VkDeviceMemory msaaImageMem = VK_NULL_HANDLE;
+ QVkReferenceRenderTarget rtWrapper;
+ QVkCommandBuffer cbWrapper;
+
+ struct ImageResources {
+ VkImage image = VK_NULL_HANDLE;
+ VkImageView imageView = VK_NULL_HANDLE;
+ VkFramebuffer fb = VK_NULL_HANDLE;
+ VkImage msaaImage = VK_NULL_HANDLE;
+ VkImageView msaaImageView = VK_NULL_HANDLE;
+ bool transferSource = false;
+ } imageRes[MAX_BUFFER_COUNT];
+
+ struct FrameResources {
+ VkFence imageFence = VK_NULL_HANDLE;
+ bool imageFenceWaitable = false;
+ VkSemaphore imageSem = VK_NULL_HANDLE;
+ VkSemaphore drawSem = VK_NULL_HANDLE;
+ bool imageAcquired = false;
+ bool imageSemWaitable = false;
+ quint32 imageIndex = 0;
+ VkCommandBuffer cmdBuf = VK_NULL_HANDLE;
+ VkFence cmdFence = VK_NULL_HANDLE;
+ bool cmdFenceWaitable = false;
+ int timestampQueryIndex = -1;
+ } frameRes[QVK_FRAMES_IN_FLIGHT];
+
+ quint32 currentImageIndex = 0; // index in imageRes
+ quint32 currentFrameSlot = 0; // index in frameRes
+ int frameCount = 0;
+
+ friend class QRhiVulkan;
+};
+
+class QRhiVulkan : public QRhiImplementation
+{
+public:
+ QRhiVulkan(QRhiVulkanInitParams *params, QRhiVulkanNativeHandles *importDevice = nullptr);
+
+ bool create(QRhi::Flags flags) override;
+ void destroy() override;
+
+ QRhiGraphicsPipeline *createGraphicsPipeline() override;
+ QRhiShaderResourceBindings *createShaderResourceBindings() override;
+ QRhiBuffer *createBuffer(QRhiBuffer::Type type,
+ QRhiBuffer::UsageFlags usage,
+ int size) override;
+ QRhiRenderBuffer *createRenderBuffer(QRhiRenderBuffer::Type type,
+ const QSize &pixelSize,
+ int sampleCount,
+ QRhiRenderBuffer::Flags flags) override;
+ QRhiTexture *createTexture(QRhiTexture::Format format,
+ const QSize &pixelSize,
+ int sampleCount,
+ QRhiTexture::Flags flags) override;
+ QRhiSampler *createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
+ QRhiSampler::Filter mipmapMode,
+ QRhiSampler:: AddressMode u, QRhiSampler::AddressMode v) override;
+
+ QRhiTextureRenderTarget *createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
+ QRhiTextureRenderTarget::Flags flags) override;
+
+ QRhiSwapChain *createSwapChain() override;
+ QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override;
+ QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) override;
+ QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb) override;
+ QRhi::FrameOpResult endOffscreenFrame() override;
+ QRhi::FrameOpResult finish() override;
+
+ void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;
+
+ void beginPass(QRhiCommandBuffer *cb,
+ QRhiRenderTarget *rt,
+ const QColor &colorClearValue,
+ const QRhiDepthStencilClearValue &depthStencilClearValue,
+ QRhiResourceUpdateBatch *resourceUpdates) override;
+ void endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;
+
+ void setGraphicsPipeline(QRhiCommandBuffer *cb,
+ QRhiGraphicsPipeline *ps) override;
+
+ void setShaderResources(QRhiCommandBuffer *cb,
+ QRhiShaderResourceBindings *srb,
+ int dynamicOffsetCount,
+ const QRhiCommandBuffer::DynamicOffset *dynamicOffsets) override;
+
+ void setVertexInput(QRhiCommandBuffer *cb,
+ int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings,
+ QRhiBuffer *indexBuf, quint32 indexOffset,
+ QRhiCommandBuffer::IndexFormat indexFormat) override;
+
+ void setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport) override;
+ void setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) override;
+ void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) override;
+ void setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) override;
+
+ void draw(QRhiCommandBuffer *cb, quint32 vertexCount,
+ quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) override;
+
+ void drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
+ quint32 instanceCount, quint32 firstIndex,
+ qint32 vertexOffset, quint32 firstInstance) override;
+
+ void debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name) override;
+ void debugMarkEnd(QRhiCommandBuffer *cb) override;
+ void debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg) override;
+
+ const QRhiNativeHandles *nativeHandles(QRhiCommandBuffer *cb) override;
+ void beginExternal(QRhiCommandBuffer *cb) override;
+ void endExternal(QRhiCommandBuffer *cb) override;
+
+ QVector<int> supportedSampleCounts() const override;
+ int ubufAlignment() const override;
+ bool isYUpInFramebuffer() const override;
+ bool isYUpInNDC() const override;
+ bool isClipDepthZeroToOne() const override;
+ QMatrix4x4 clipSpaceCorrMatrix() const override;
+ bool isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const override;
+ bool isFeatureSupported(QRhi::Feature feature) const override;
+ int resourceLimit(QRhi::ResourceLimit limit) const override;
+ const QRhiNativeHandles *nativeHandles() override;
+ void sendVMemStatsToProfiler() override;
+
+ VkResult createDescriptorPool(VkDescriptorPool *pool);
+ bool allocateDescriptorSet(VkDescriptorSetAllocateInfo *allocInfo, VkDescriptorSet *result, int *resultPoolIndex);
+ uint32_t chooseTransientImageMemType(VkImage img, uint32_t startIndex);
+ bool createTransientImage(VkFormat format, const QSize &pixelSize, VkImageUsageFlags usage,
+ VkImageAspectFlags aspectMask, VkSampleCountFlagBits samples,
+ VkDeviceMemory *mem, VkImage *images, VkImageView *views, int count);
+
+ bool recreateSwapChain(QRhiSwapChain *swapChain);
+ void releaseSwapChainResources(QRhiSwapChain *swapChain);
+
+ VkFormat optimalDepthStencilFormat();
+ VkSampleCountFlagBits effectiveSampleCount(int sampleCount);
+ bool createDefaultRenderPass(VkRenderPass *rp,
+ bool hasDepthStencil,
+ VkSampleCountFlagBits samples,
+ VkFormat colorFormat);
+ bool createOffscreenRenderPass(VkRenderPass *rp,
+ const QVector<QRhiColorAttachment> &colorAttachments,
+ bool preserveColor,
+ bool preserveDs,
+ QRhiRenderBuffer *depthStencilBuffer,
+ QRhiTexture *depthTexture);
+ bool ensurePipelineCache();
+ VkShaderModule createShader(const QByteArray &spirv);
+
+ void prepareNewFrame(QRhiCommandBuffer *cb);
+ QRhi::FrameOpResult startCommandBuffer(VkCommandBuffer *cb);
+ QRhi::FrameOpResult endAndSubmitCommandBuffer(VkCommandBuffer cb, VkFence cmdFence,
+ VkSemaphore *waitSem, VkSemaphore *signalSem);
+ void waitCommandCompletion(int frameSlot);
+ VkDeviceSize subresUploadByteSize(const QRhiTextureSubresourceUploadDescription &subresDesc) const;
+ using BufferImageCopyList = QVarLengthArray<VkBufferImageCopy, 16>;
+ void prepareUploadSubres(QVkTexture *texD, int layer, int level,
+ const QRhiTextureSubresourceUploadDescription &subresDesc,
+ size_t *curOfs, void *mp,
+ BufferImageCopyList *copyInfos);
+ void enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdateBatch *resourceUpdates);
+ void executeBufferHostWritesForCurrentFrame(QVkBuffer *bufD);
+ void enqueueTransitionPassResources(QVkCommandBuffer *cbD);
+ void recordCommandBuffer(QVkCommandBuffer *cbD);
+ void trackedRegisterBuffer(QRhiPassResourceTracker *passResTracker,
+ QVkBuffer *bufD,
+ int slot,
+ QRhiPassResourceTracker::BufferAccess access,
+ QRhiPassResourceTracker::BufferStage stage);
+ void trackedRegisterTexture(QRhiPassResourceTracker *passResTracker,
+ QVkTexture *texD,
+ QRhiPassResourceTracker::TextureAccess access,
+ QRhiPassResourceTracker::TextureStage stage);
+ void recordTransitionPassResources(QVkCommandBuffer *cbD, const QRhiPassResourceTracker &tracker);
+ void activateTextureRenderTarget(QVkCommandBuffer *cbD, QVkTextureRenderTarget *rtD);
+ void executeDeferredReleases(bool forced = false);
+ void finishActiveReadbacks(bool forced = false);
+
+ void setObjectName(uint64_t object, VkDebugReportObjectTypeEXT type, const QByteArray &name, int slot = -1);
+ void trackedBufferBarrier(QVkCommandBuffer *cbD, QVkBuffer *bufD, int slot,
+ VkAccessFlags access, VkPipelineStageFlags stage);
+ void trackedImageBarrier(QVkCommandBuffer *cbD, QVkTexture *texD,
+ VkImageLayout layout, VkAccessFlags access, VkPipelineStageFlags stage);
+ void subresourceBarrier(QVkCommandBuffer *cbD, VkImage image,
+ VkImageLayout oldLayout, VkImageLayout newLayout,
+ VkAccessFlags srcAccess, VkAccessFlags dstAccess,
+ VkPipelineStageFlags srcStage, VkPipelineStageFlags dstStage,
+ int startLayer, int layerCount,
+ int startLevel, int levelCount);
+ void updateShaderResourceBindings(QRhiShaderResourceBindings *srb, int descSetIdx = -1);
+
+ QVulkanInstance *inst = nullptr;
+ QWindow *maybeWindow = nullptr;
+ bool importedDevice = false;
+ VkPhysicalDevice physDev = VK_NULL_HANDLE;
+ VkDevice dev = VK_NULL_HANDLE;
+ bool importedCmdPool = false;
+ VkCommandPool cmdPool = VK_NULL_HANDLE;
+ int gfxQueueFamilyIdx = -1;
+ VkQueue gfxQueue = VK_NULL_HANDLE;
+ quint32 timestampValidBits = 0;
+ bool importedAllocator = false;
+ QVkAllocator allocator = nullptr;
+ QVulkanFunctions *f = nullptr;
+ QVulkanDeviceFunctions *df = nullptr;
+ VkPhysicalDeviceProperties physDevProperties;
+ VkDeviceSize ubufAlign;
+ VkDeviceSize texbufAlign;
+
+ bool debugMarkersAvailable = false;
+ bool vertexAttribDivisorAvailable = false;
+ PFN_vkCmdDebugMarkerBeginEXT vkCmdDebugMarkerBegin = nullptr;
+ PFN_vkCmdDebugMarkerEndEXT vkCmdDebugMarkerEnd = nullptr;
+ PFN_vkCmdDebugMarkerInsertEXT vkCmdDebugMarkerInsert = nullptr;
+ PFN_vkDebugMarkerSetObjectNameEXT vkDebugMarkerSetObjectName = nullptr;
+
+ PFN_vkCreateSwapchainKHR vkCreateSwapchainKHR = nullptr;
+ PFN_vkDestroySwapchainKHR vkDestroySwapchainKHR;
+ PFN_vkGetSwapchainImagesKHR vkGetSwapchainImagesKHR;
+ PFN_vkAcquireNextImageKHR vkAcquireNextImageKHR;
+ PFN_vkQueuePresentKHR vkQueuePresentKHR;
+ PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vkGetPhysicalDeviceSurfaceCapabilitiesKHR = nullptr;
+ PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vkGetPhysicalDeviceSurfaceFormatsKHR;
+ PFN_vkGetPhysicalDeviceSurfacePresentModesKHR vkGetPhysicalDeviceSurfacePresentModesKHR;
+
+ VkPipelineCache pipelineCache = VK_NULL_HANDLE;
+ struct DescriptorPoolData {
+ DescriptorPoolData() { }
+ DescriptorPoolData(VkDescriptorPool pool_)
+ : pool(pool_)
+ { }
+ VkDescriptorPool pool = VK_NULL_HANDLE;
+ int refCount = 0;
+ int allocedDescSets = 0;
+ };
+ QVector<DescriptorPoolData> descriptorPools;
+
+ VkQueryPool timestampQueryPool = VK_NULL_HANDLE;
+ QBitArray timestampQueryPoolMap;
+
+ VkFormat optimalDsFormat = VK_FORMAT_UNDEFINED;
+ QMatrix4x4 clipCorrectMatrix;
+
+ bool inFrame = false;
+ bool inPass = false;
+ QVkSwapChain *currentSwapChain = nullptr;
+ QSet<QVkSwapChain *> swapchains;
+ QRhiVulkanNativeHandles nativeHandlesStruct;
+
+ struct OffscreenFrame {
+ OffscreenFrame(QRhiImplementation *rhi) : cbWrapper(rhi) { }
+ bool active = false;
+ QVkCommandBuffer cbWrapper;
+ VkFence cmdFence = VK_NULL_HANDLE;
+ } ofr;
+
+ struct ActiveReadback {
+ int activeFrameSlot = -1;
+ QRhiReadbackDescription desc;
+ QRhiReadbackResult *result;
+ VkBuffer buf;
+ QVkAlloc bufAlloc;
+ quint32 bufSize;
+ QSize pixelSize;
+ QRhiTexture::Format format;
+ };
+ QVector<ActiveReadback> activeReadbacks;
+
+ struct DeferredReleaseEntry {
+ enum Type {
+ Pipeline,
+ ShaderResourceBindings,
+ Buffer,
+ RenderBuffer,
+ Texture,
+ Sampler,
+ TextureRenderTarget,
+ RenderPass,
+ StagingBuffer
+ };
+ Type type;
+ int lastActiveFrameSlot; // -1 if not used otherwise 0..FRAMES_IN_FLIGHT-1
+ union {
+ struct {
+ VkPipeline pipeline;
+ VkPipelineLayout layout;
+ } pipelineState;
+ struct {
+ int poolIndex;
+ VkDescriptorSetLayout layout;
+ } shaderResourceBindings;
+ struct {
+ VkBuffer buffers[QVK_FRAMES_IN_FLIGHT];
+ QVkAlloc allocations[QVK_FRAMES_IN_FLIGHT];
+ VkBuffer stagingBuffers[QVK_FRAMES_IN_FLIGHT];
+ QVkAlloc stagingAllocations[QVK_FRAMES_IN_FLIGHT];
+ } buffer;
+ struct {
+ VkDeviceMemory memory;
+ VkImage image;
+ VkImageView imageView;
+ } renderBuffer;
+ struct {
+ VkImage image;
+ VkImageView imageView;
+ QVkAlloc allocation;
+ VkBuffer stagingBuffers[QVK_FRAMES_IN_FLIGHT];
+ QVkAlloc stagingAllocations[QVK_FRAMES_IN_FLIGHT];
+ } texture;
+ struct {
+ VkSampler sampler;
+ } sampler;
+ struct {
+ VkFramebuffer fb;
+ VkImageView rtv[QVkRenderTargetData::MAX_COLOR_ATTACHMENTS];
+ VkImageView resrtv[QVkRenderTargetData::MAX_COLOR_ATTACHMENTS];
+ } textureRenderTarget;
+ struct {
+ VkRenderPass rp;
+ } renderPass;
+ struct {
+ VkBuffer stagingBuffer;
+ QVkAlloc stagingAllocation;
+ } stagingBuffer;
+ };
+ };
+ QVector<DeferredReleaseEntry> releaseQueue;
+};
+
+Q_DECLARE_TYPEINFO(QRhiVulkan::DescriptorPoolData, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(QRhiVulkan::DeferredReleaseEntry, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(QRhiVulkan::ActiveReadback, Q_MOVABLE_TYPE);
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/rhi/qrhivulkanext_p.h b/src/gui/rhi/qrhivulkanext_p.h
new file mode 100644
index 0000000000..67a63e07e0
--- /dev/null
+++ b/src/gui/rhi/qrhivulkanext_p.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt RHI module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QRHIVULKANEXT_P_H
+#define QRHIVULKANEXT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qrhivulkan_p.h"
+
+QT_BEGIN_NAMESPACE
+
+#ifndef VK_EXT_vertex_attribute_divisor
+#define VK_EXT_vertex_attribute_divisor 1
+#define VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_SPEC_VERSION 2
+#define VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME "VK_EXT_vertex_attribute_divisor"
+
+typedef struct VkPhysicalDeviceVertexAttributeDivisorPropertiesEXT {
+ VkStructureType sType;
+ void* pNext;
+ uint32_t maxVertexAttribDivisor;
+} VkPhysicalDeviceVertexAttributeDivisorPropertiesEXT;
+
+typedef struct VkVertexInputBindingDivisorDescriptionEXT {
+ uint32_t binding;
+ uint32_t divisor;
+} VkVertexInputBindingDivisorDescriptionEXT;
+
+typedef struct VkPipelineVertexInputDivisorStateCreateInfoEXT {
+ VkStructureType sType;
+ const void* pNext;
+ uint32_t vertexBindingDivisorCount;
+ const VkVertexInputBindingDivisorDescriptionEXT* pVertexBindingDivisors;
+} VkPipelineVertexInputDivisorStateCreateInfoEXT;
+#endif // VK_EXT_vertex_attribute_divisor
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/rhi/qshader.cpp b/src/gui/rhi/qshader.cpp
new file mode 100644
index 0000000000..5569aad639
--- /dev/null
+++ b/src/gui/rhi/qshader.cpp
@@ -0,0 +1,586 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gui module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qshader_p_p.h"
+#include <QDataStream>
+#include <QBuffer>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QShader
+ \inmodule QtRhi
+ \since 5.14
+
+ \brief Contains multiple versions of a shader translated to multiple shading languages,
+ together with reflection metadata.
+
+ QShader is the entry point to shader code in the graphics API agnostic
+ Qt world. Instead of using GLSL shader sources, as was the custom with Qt
+ 5.x, new graphics systems with backends for multiple graphics APIs, such
+ as, Vulkan, Metal, Direct3D, and OpenGL, take QShader as their input
+ whenever a shader needs to be specified.
+
+ A QShader instance is empty and thus invalid by default. To get a useful
+ instance, the two typical methods are:
+
+ \list
+
+ \li Generate the contents offline, during build time or earlier, using the
+ \c qsb command line tool. The result is a binary file that is shipped with
+ the application, read via QIODevice::readAll(), and then deserialized via
+ fromSerialized(). For more information, see QShaderBaker.
+
+ \li Generate at run time via QShaderBaker. This is an expensive operation,
+ but allows applications to use user-provided or dynamically generated
+ shader source strings.
+
+ \endlist
+
+ When used together with the Qt Rendering Hardware Interface and its
+ classes, like QRhiGraphicsPipeline, no further action is needed from the
+ application's side as these classes are prepared to consume a QShader
+ whenever a shader needs to be specified for a given stage of the graphics
+ pipeline.
+
+ Alternatively, applications can access
+
+ \list
+
+ \li the source or byte code for any of the shading language versions that
+ are included in the QShader,
+
+ \li the name of the entry point for the shader,
+
+ \li the reflection metadata containing a description of the shader's
+ inputs, outputs and resources like uniform blocks. This is essential when
+ an application or framework needs to discover the inputs of a shader at
+ runtime due to not having advance knowledge of the vertex attributes or the
+ layout of the uniform buffers used by the shader.
+
+ \endlist
+
+ QShader makes no assumption about the shading language that was used
+ as the source for generating the various versions and variants that are
+ included in it.
+
+ QShader uses implicit sharing similarly to many core Qt types, and so
+ can be returned or passed by value. Detach happens implicitly when calling
+ a setter.
+
+ For reference, QRhi expects that a QShader suitable for all its
+ backends contains at least the following:
+
+ \list
+
+ \li SPIR-V 1.0 bytecode suitable for Vulkan 1.0 or newer
+
+ \li GLSL/ES 100 source code suitable for OpenGL ES 2.0 or newer
+
+ \li GLSL 120 source code suitable for OpenGL 2.1
+
+ \li HLSL Shader Model 5.0 source code or the corresponding DXBC bytecode suitable for Direct3D 11
+
+ \li Metal Shading Language 1.2 source code or the corresponding bytecode suitable for Metal
+
+ \endlist
+
+ \sa QShaderBaker
+ */
+
+/*!
+ \enum QShader::Stage
+ Describes the stage of the graphics pipeline the shader is suitable for.
+
+ \value VertexStage Vertex shader
+ \value TessellationControlStage Tessellation control (hull) shader
+ \value TessellationEvaluationStage Tessellation evaluation (domain) shader
+ \value GeometryStage Geometry shader
+ \value FragmentStage Fragment (pixel) shader
+ \value ComputeStage Compute shader
+ */
+
+/*!
+ \class QShaderVersion
+ \inmodule QtRhi
+
+ \brief Specifies the shading language version.
+
+ While languages like SPIR-V or the Metal Shading Language use traditional
+ version numbers, shaders for other APIs can use slightly different
+ versioning schemes. All those are mapped to a single version number in
+ here, however. For HLSL, the version refers to the Shader Model version,
+ like 5.0, 5.1, or 6.0. For GLSL an additional flag is needed to choose
+ between GLSL and GLSL/ES.
+
+ Below is a list with the most common examples of shader versions for
+ different graphics APIs:
+
+ \list
+
+ \li Vulkan (SPIR-V): 100
+ \li OpenGL: 120, 330, 440, etc.
+ \li OpenGL ES: 100 with GlslEs, 300 with GlslEs, etc.
+ \li Direct3D: 50, 51, 60
+ \li Metal: 12, 20
+ \endlist
+
+ A default constructed QShaderVersion contains a version of 100 and no
+ flags set.
+ */
+
+/*!
+ \enum QShaderVersion::Flag
+
+ Describes the flags that can be set.
+
+ \value GlslEs Indicates that GLSL/ES is meant in combination with GlslShader
+ */
+
+/*!
+ \class QShaderKey
+ \inmodule QtRhi
+
+ \brief Specifies the shading language, the version with flags, and the variant.
+
+ A default constructed QShaderKey has source set to SpirvShader and
+ sourceVersion set to 100. sourceVariant defaults to StandardShader.
+ */
+
+/*!
+ \enum QShader::Source
+ Describes what kind of shader code an entry contains.
+
+ \value SpirvShader SPIR-V
+ \value GlslShader GLSL
+ \value HlslShader HLSL
+ \value DxbcShader Direct3D bytecode (HLSL compiled by \c fxc)
+ \value MslShader Metal Shading Language
+ \value DxilShader Direct3D bytecode (HLSL compiled by \c dxc)
+ \value MetalLibShader Pre-compiled Metal bytecode
+ */
+
+/*!
+ \enum QShader::Variant
+ Describes what kind of shader code an entry contains.
+
+ \value StandardShader The normal, unmodified version of the shader code.
+ \value BatchableVertexShader Vertex shader rewritten to be suitable for Qt Quick scenegraph batching.
+ */
+
+/*!
+ \class QShaderCode
+ \inmodule QtRhi
+
+ \brief Contains source or binary code for a shader and additional metadata.
+
+ When shader() is empty after retrieving a QShaderCode instance from
+ QShader, it indicates no shader code was found for the requested key.
+ */
+
+static const int QSB_VERSION = 1;
+
+/*!
+ Constructs a new, empty (and thus invalid) QShader instance.
+ */
+QShader::QShader()
+ : d(new QShaderPrivate)
+{
+}
+
+/*!
+ \internal
+ */
+void QShader::detach()
+{
+ qAtomicDetach(d);
+}
+
+/*!
+ \internal
+ */
+QShader::QShader(const QShader &other)
+ : d(other.d)
+{
+ d->ref.ref();
+}
+
+/*!
+ \internal
+ */
+QShader &QShader::operator=(const QShader &other)
+{
+ qAtomicAssign(d, other.d);
+ return *this;
+}
+
+/*!
+ Destructor.
+ */
+QShader::~QShader()
+{
+ if (!d->ref.deref())
+ delete d;
+}
+
+/*!
+ \return true if the QShader contains at least one shader version.
+ */
+bool QShader::isValid() const
+{
+ return !d->shaders.isEmpty();
+}
+
+/*!
+ \return the pipeline stage the shader is meant for.
+ */
+QShader::Stage QShader::stage() const
+{
+ return d->stage;
+}
+
+/*!
+ Sets the pipeline \a stage.
+ */
+void QShader::setStage(Stage stage)
+{
+ if (stage != d->stage) {
+ detach();
+ d->stage = stage;
+ }
+}
+
+/*!
+ \return the reflection metadata for the shader.
+ */
+QShaderDescription QShader::description() const
+{
+ return d->desc;
+}
+
+/*!
+ Sets the reflection metadata to \a desc.
+ */
+void QShader::setDescription(const QShaderDescription &desc)
+{
+ detach();
+ d->desc = desc;
+}
+
+/*!
+ \return the list of available shader versions
+ */
+QVector<QShaderKey> QShader::availableShaders() const
+{
+ return d->shaders.keys().toVector();
+}
+
+/*!
+ \return the source or binary code for a given shader version specified by \a key.
+ */
+QShaderCode QShader::shader(const QShaderKey &key) const
+{
+ return d->shaders.value(key);
+}
+
+/*!
+ Stores the source or binary \a shader code for a given shader version specified by \a key.
+ */
+void QShader::setShader(const QShaderKey &key, const QShaderCode &shader)
+{
+ if (d->shaders.value(key) == shader)
+ return;
+
+ detach();
+ d->shaders[key] = shader;
+}
+
+/*!
+ Removes the source or binary shader code for a given \a key.
+ Does nothing when not found.
+ */
+void QShader::removeShader(const QShaderKey &key)
+{
+ auto it = d->shaders.find(key);
+ if (it == d->shaders.end())
+ return;
+
+ detach();
+ d->shaders.erase(it);
+}
+
+/*!
+ \return a serialized binary version of all the data held by the
+ QShader, suitable for writing to files or other I/O devices.
+
+ \sa fromSerialized()
+ */
+QByteArray QShader::serialized() const
+{
+ QBuffer buf;
+ QDataStream ds(&buf);
+ ds.setVersion(QDataStream::Qt_5_10);
+ if (!buf.open(QIODevice::WriteOnly))
+ return QByteArray();
+
+ ds << QSB_VERSION;
+ ds << d->stage;
+ ds << d->desc.toBinaryJson();
+ ds << d->shaders.count();
+ for (auto it = d->shaders.cbegin(), itEnd = d->shaders.cend(); it != itEnd; ++it) {
+ const QShaderKey &k(it.key());
+ ds << k.source();
+ ds << k.sourceVersion().version();
+ ds << k.sourceVersion().flags();
+ ds << k.sourceVariant();
+ const QShaderCode &shader(d->shaders.value(k));
+ ds << shader.shader();
+ ds << shader.entryPoint();
+ }
+
+ return qCompress(buf.buffer());
+}
+
+/*!
+ Creates a new QShader instance from the given \a data.
+
+ \sa serialized()
+ */
+QShader QShader::fromSerialized(const QByteArray &data)
+{
+ QByteArray udata = qUncompress(data);
+ QBuffer buf(&udata);
+ QDataStream ds(&buf);
+ ds.setVersion(QDataStream::Qt_5_10);
+ if (!buf.open(QIODevice::ReadOnly))
+ return QShader();
+
+ QShader bs;
+ QShaderPrivate *d = QShaderPrivate::get(&bs);
+ Q_ASSERT(d->ref.load() == 1); // must be detached
+ int intVal;
+ ds >> intVal;
+ if (intVal != QSB_VERSION)
+ return QShader();
+
+ ds >> intVal;
+ d->stage = Stage(intVal);
+ QByteArray descBin;
+ ds >> descBin;
+ d->desc = QShaderDescription::fromBinaryJson(descBin);
+ int count;
+ ds >> count;
+ for (int i = 0; i < count; ++i) {
+ QShaderKey k;
+ ds >> intVal;
+ k.setSource(Source(intVal));
+ QShaderVersion ver;
+ ds >> intVal;
+ ver.setVersion(intVal);
+ ds >> intVal;
+ ver.setFlags(QShaderVersion::Flags(intVal));
+ k.setSourceVersion(ver);
+ ds >> intVal;
+ k.setSourceVariant(Variant(intVal));
+ QShaderCode shader;
+ QByteArray s;
+ ds >> s;
+ shader.setShader(s);
+ ds >> s;
+ shader.setEntryPoint(s);
+ d->shaders[k] = shader;
+ }
+
+ return bs;
+}
+
+QShaderVersion::QShaderVersion(int v, Flags f)
+ : m_version(v), m_flags(f)
+{
+}
+
+QShaderCode::QShaderCode(const QByteArray &code, const QByteArray &entry)
+ : m_shader(code), m_entryPoint(entry)
+{
+}
+
+QShaderKey::QShaderKey(QShader::Source s,
+ const QShaderVersion &sver,
+ QShader::Variant svar)
+ : m_source(s),
+ m_sourceVersion(sver),
+ m_sourceVariant(svar)
+{
+}
+
+/*!
+ Returns \c true if the two QShader objects \a a and \a b are equal,
+ meaning they are for the same stage with matching sets of shader source or
+ binary code.
+
+ \relates QShader
+ */
+bool operator==(const QShader &lhs, const QShader &rhs) Q_DECL_NOTHROW
+{
+ return lhs.d->stage == rhs.d->stage
+ && lhs.d->shaders == rhs.d->shaders;
+ // do not bother with desc, if the shader code is the same, the description must match too
+}
+
+/*!
+ \fn bool operator!=(const QShader &lhs, const QShader &rhs)
+
+ Returns \c false if the values in the two QShader objects \a a and \a b
+ are equal; otherwise returns \c true.
+
+ \relates QShader
+ */
+
+/*!
+ Returns the hash value for \a s, using \a seed to seed the calculation.
+
+ \relates QShader
+ */
+uint qHash(const QShader &s, uint seed) Q_DECL_NOTHROW
+{
+ uint h = s.stage();
+ for (auto it = s.d->shaders.constBegin(), itEnd = s.d->shaders.constEnd(); it != itEnd; ++it)
+ h += qHash(it.key(), seed) + qHash(it.value().shader(), seed);
+ return h;
+}
+
+/*!
+ Returns \c true if the two QShaderVersion objects \a a and \a b are
+ equal.
+
+ \relates QShaderVersion
+ */
+bool operator==(const QShaderVersion &lhs, const QShaderVersion &rhs) Q_DECL_NOTHROW
+{
+ return lhs.version() == rhs.version() && lhs.flags() == rhs.flags();
+}
+
+/*!
+ \fn bool operator!=(const QShaderVersion &lhs, const QShaderVersion &rhs)
+
+ Returns \c false if the values in the two QShaderVersion objects \a a
+ and \a b are equal; otherwise returns \c true.
+
+ \relates QShaderVersion
+ */
+
+/*!
+ Returns \c true if the two QShaderKey objects \a a and \a b are equal.
+
+ \relates QShaderKey
+ */
+bool operator==(const QShaderKey &lhs, const QShaderKey &rhs) Q_DECL_NOTHROW
+{
+ return lhs.source() == rhs.source() && lhs.sourceVersion() == rhs.sourceVersion()
+ && lhs.sourceVariant() == rhs.sourceVariant();
+}
+
+/*!
+ \fn bool operator!=(const QShaderKey &lhs, const QShaderKey &rhs)
+
+ Returns \c false if the values in the two QShaderKey objects \a a
+ and \a b are equal; otherwise returns \c true.
+
+ \relates QShaderKey
+ */
+
+/*!
+ Returns the hash value for \a k, using \a seed to seed the calculation.
+
+ \relates QShaderKey
+ */
+uint qHash(const QShaderKey &k, uint seed) Q_DECL_NOTHROW
+{
+ return seed + 10 * k.source() + k.sourceVersion().version() + k.sourceVersion().flags() + k.sourceVariant();
+}
+
+/*!
+ Returns \c true if the two QShaderCode objects \a a and \a b are equal.
+
+ \relates QShaderCode
+ */
+bool operator==(const QShaderCode &lhs, const QShaderCode &rhs) Q_DECL_NOTHROW
+{
+ return lhs.shader() == rhs.shader() && lhs.entryPoint() == rhs.entryPoint();
+}
+
+/*!
+ \fn bool operator!=(const QShaderCode &lhs, const QShaderCode &rhs)
+
+ Returns \c false if the values in the two QShaderCode objects \a a
+ and \a b are equal; otherwise returns \c true.
+
+ \relates QShaderCode
+ */
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug dbg, const QShader &bs)
+{
+ const QShaderPrivate *d = bs.d;
+ QDebugStateSaver saver(dbg);
+
+ dbg.nospace() << "QShader("
+ << "stage=" << d->stage
+ << " shaders=" << d->shaders.keys()
+ << " desc.isValid=" << d->desc.isValid()
+ << ')';
+
+ return dbg;
+}
+
+QDebug operator<<(QDebug dbg, const QShaderKey &k)
+{
+ QDebugStateSaver saver(dbg);
+ dbg.nospace() << "ShaderKey(" << k.source()
+ << " " << k.sourceVersion()
+ << " " << k.sourceVariant() << ")";
+ return dbg;
+}
+
+QDebug operator<<(QDebug dbg, const QShaderVersion &v)
+{
+ QDebugStateSaver saver(dbg);
+ dbg.nospace() << "Version(" << v.version() << " " << v.flags() << ")";
+ return dbg;
+}
+#endif // QT_NO_DEBUG_STREAM
+
+QT_END_NAMESPACE
diff --git a/src/gui/rhi/qshader_p.h b/src/gui/rhi/qshader_p.h
new file mode 100644
index 0000000000..12a0a26e12
--- /dev/null
+++ b/src/gui/rhi/qshader_p.h
@@ -0,0 +1,227 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gui module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSHADER_P_H
+#define QSHADER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of a number of Qt sources files. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtGui/qtguiglobal.h>
+#include <private/qshaderdescription_p.h>
+
+QT_BEGIN_NAMESPACE
+
+struct QShaderPrivate;
+class QShaderKey;
+
+class Q_GUI_EXPORT QShaderVersion
+{
+public:
+ enum Flag {
+ GlslEs = 0x01
+ };
+ Q_DECLARE_FLAGS(Flags, Flag)
+
+ QShaderVersion() = default;
+ QShaderVersion(int v, Flags f = Flags());
+
+ int version() const { return m_version; }
+ void setVersion(int v) { m_version = v; }
+
+ Flags flags() const { return m_flags; }
+ void setFlags(Flags f) { m_flags = f; }
+
+private:
+ int m_version = 100;
+ Flags m_flags;
+ Q_DECL_UNUSED_MEMBER quint64 m_reserved = 0;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QShaderVersion::Flags)
+Q_DECLARE_TYPEINFO(QShaderVersion, Q_MOVABLE_TYPE);
+
+class Q_GUI_EXPORT QShaderCode
+{
+public:
+ QShaderCode() = default;
+ QShaderCode(const QByteArray &code, const QByteArray &entry = QByteArray());
+
+ QByteArray shader() const { return m_shader; }
+ void setShader(const QByteArray &code) { m_shader = code; }
+
+ QByteArray entryPoint() const { return m_entryPoint; }
+ void setEntryPoint(const QByteArray &entry) { m_entryPoint = entry; }
+
+private:
+ QByteArray m_shader;
+ QByteArray m_entryPoint;
+ Q_DECL_UNUSED_MEMBER quint64 m_reserved = 0;
+};
+
+Q_DECLARE_TYPEINFO(QShaderCode, Q_MOVABLE_TYPE);
+
+class Q_GUI_EXPORT QShader
+{
+public:
+ enum Stage {
+ VertexStage = 0,
+ TessellationControlStage,
+ TessellationEvaluationStage,
+ GeometryStage,
+ FragmentStage,
+ ComputeStage
+ };
+
+ enum Source {
+ SpirvShader = 0,
+ GlslShader,
+ HlslShader,
+ DxbcShader, // fxc
+ MslShader,
+ DxilShader, // dxc
+ MetalLibShader // xcrun metal + xcrun metallib
+ };
+
+ enum Variant {
+ StandardShader = 0,
+ BatchableVertexShader
+ };
+
+ QShader();
+ QShader(const QShader &other);
+ QShader &operator=(const QShader &other);
+ ~QShader();
+ void detach();
+
+ bool isValid() const;
+
+ Stage stage() const;
+ void setStage(Stage stage);
+
+ QShaderDescription description() const;
+ void setDescription(const QShaderDescription &desc);
+
+ QVector<QShaderKey> availableShaders() const;
+ QShaderCode shader(const QShaderKey &key) const;
+ void setShader(const QShaderKey &key, const QShaderCode &shader);
+ void removeShader(const QShaderKey &key);
+
+ QByteArray serialized() const;
+ static QShader fromSerialized(const QByteArray &data);
+
+private:
+ QShaderPrivate *d;
+ friend struct QShaderPrivate;
+ friend Q_GUI_EXPORT bool operator==(const QShader &, const QShader &) Q_DECL_NOTHROW;
+ friend Q_GUI_EXPORT uint qHash(const QShader &, uint) Q_DECL_NOTHROW;
+#ifndef QT_NO_DEBUG_STREAM
+ friend Q_GUI_EXPORT QDebug operator<<(QDebug, const QShader &);
+#endif
+};
+
+class Q_GUI_EXPORT QShaderKey
+{
+public:
+ QShaderKey() = default;
+ QShaderKey(QShader::Source s,
+ const QShaderVersion &sver,
+ QShader::Variant svar = QShader::StandardShader);
+
+ QShader::Source source() const { return m_source; }
+ void setSource(QShader::Source s) { m_source = s; }
+
+ QShaderVersion sourceVersion() const { return m_sourceVersion; }
+ void setSourceVersion(const QShaderVersion &sver) { m_sourceVersion = sver; }
+
+ QShader::Variant sourceVariant() const { return m_sourceVariant; }
+ void setSourceVariant(QShader::Variant svar) { m_sourceVariant = svar; }
+
+private:
+ QShader::Source m_source = QShader::SpirvShader;
+ QShaderVersion m_sourceVersion;
+ QShader::Variant m_sourceVariant = QShader::StandardShader;
+ Q_DECL_UNUSED_MEMBER quint64 m_reserved = 0;
+};
+
+Q_DECLARE_TYPEINFO(QShaderKey, Q_MOVABLE_TYPE);
+
+Q_GUI_EXPORT bool operator==(const QShader &lhs, const QShader &rhs) Q_DECL_NOTHROW;
+Q_GUI_EXPORT uint qHash(const QShader &s, uint seed = 0) Q_DECL_NOTHROW;
+
+inline bool operator!=(const QShader &lhs, const QShader &rhs) Q_DECL_NOTHROW
+{
+ return !(lhs == rhs);
+}
+
+Q_GUI_EXPORT bool operator==(const QShaderVersion &lhs, const QShaderVersion &rhs) Q_DECL_NOTHROW;
+Q_GUI_EXPORT bool operator==(const QShaderKey &lhs, const QShaderKey &rhs) Q_DECL_NOTHROW;
+Q_GUI_EXPORT bool operator==(const QShaderCode &lhs, const QShaderCode &rhs) Q_DECL_NOTHROW;
+
+inline bool operator!=(const QShaderVersion &lhs, const QShaderVersion &rhs) Q_DECL_NOTHROW
+{
+ return !(lhs == rhs);
+}
+
+inline bool operator!=(const QShaderKey &lhs, const QShaderKey &rhs) Q_DECL_NOTHROW
+{
+ return !(lhs == rhs);
+}
+
+inline bool operator!=(const QShaderCode &lhs, const QShaderCode &rhs) Q_DECL_NOTHROW
+{
+ return !(lhs == rhs);
+}
+
+Q_GUI_EXPORT uint qHash(const QShaderKey &k, uint seed = 0) Q_DECL_NOTHROW;
+
+#ifndef QT_NO_DEBUG_STREAM
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QShader &);
+Q_GUI_EXPORT QDebug operator<<(QDebug dbg, const QShaderKey &k);
+Q_GUI_EXPORT QDebug operator<<(QDebug dbg, const QShaderVersion &v);
+#endif
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/rhi/qshader_p_p.h b/src/gui/rhi/qshader_p_p.h
new file mode 100644
index 0000000000..6473590e95
--- /dev/null
+++ b/src/gui/rhi/qshader_p_p.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gui module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSHADER_P_P_H
+#define QSHADER_P_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of a number of Qt sources files. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qshader_p.h"
+#include <QtCore/QAtomicInt>
+#include <QtCore/QHash>
+#include <QtCore/QDebug>
+
+QT_BEGIN_NAMESPACE
+
+struct Q_GUI_EXPORT QShaderPrivate
+{
+ QShaderPrivate()
+ : ref(1)
+ {
+ }
+
+ QShaderPrivate(const QShaderPrivate *other)
+ : ref(1),
+ stage(other->stage),
+ desc(other->desc),
+ shaders(other->shaders)
+ {
+ }
+
+ static QShaderPrivate *get(QShader *s) { return s->d; }
+ static const QShaderPrivate *get(const QShader *s) { return s->d; }
+
+ QAtomicInt ref;
+ QShader::Stage stage = QShader::VertexStage;
+ QShaderDescription desc;
+ QHash<QShaderKey, QShaderCode> shaders;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/rhi/qshaderdescription.cpp b/src/gui/rhi/qshaderdescription.cpp
new file mode 100644
index 0000000000..ed549b083f
--- /dev/null
+++ b/src/gui/rhi/qshaderdescription.cpp
@@ -0,0 +1,1088 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gui module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qshaderdescription_p_p.h"
+#include <QDebug>
+#include <QJsonObject>
+#include <QJsonArray>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QShaderDescription
+ \inmodule QtRhi
+ \since 5.14
+
+ \brief Describes the interface of a shader.
+
+ A shader typically has a set of inputs and outputs. A vertex shader for
+ example has a number of input variables and may use one or more uniform
+ buffers to access data (e.g. a modelview matrix) provided by the
+ application. The shader for the fragment stage receives data from the
+ vertex stage (in a simple setup) and may also rely on data from uniform
+ buffers, images, and samplers.
+
+ When it comes to vertex inputs and the layout of the uniform buffers (what
+ are the names of the members? what is there size, offset, and so on),
+ applications and frameworks may need to discover this dynamically at run
+ time. This is typical when the shader is not built-in but provided by an
+ external entity, like the user.
+
+ Modern and lean graphics APIs may no longer provide a way to query shader
+ reflection information at run time. Therefore, such data is now
+ automatically generated by QShaderBaker and is provided as a
+ QShaderDescription object for each and every QShader.
+
+ \section2 Example
+
+ Take the following vertex shader:
+
+ \badcode
+ #version 440
+
+ layout(location = 0) in vec4 position;
+ layout(location = 1) in vec3 color;
+ layout(location = 0) out vec3 v_color;
+
+ layout(std140, binding = 0) uniform buf {
+ mat4 mvp;
+ float opacity;
+ } ubuf;
+
+ out gl_PerVertex { vec4 gl_Position; };
+
+ void main()
+ {
+ v_color = color;
+ gl_Position = ubuf.mvp * position;
+ }
+ \endcode
+
+ This shader has two inputs: \c position at location 0 with a type of \c
+ vec4, and \c color at location 1 with a type of \c vec3. It has one output:
+ \c v_color, although this is typically not interesting for applications.
+ What is more important, there is a uniform block at binding 0 with a size
+ of 68 bytes and two members, a 4x4 matrix named \c mvp at offset 0, and a
+ float \c opacity at offset 64.
+
+ All this is described by a QShaderDescription object. QShaderDescription
+ can also be serialized to JSON and binary JSON, and can be deserialized
+ from binary JSON. In practice this is rarely needed since QShader
+ takes care of the associated QShaderDescription automatically, but if the
+ QShaderDescription of the above shader would be written out as JSON, it
+ would look like the following:
+
+ \badcode
+ {
+ "inputs": [
+ {
+ "location": 1,
+ "name": "color",
+ "type": "vec3"
+ },
+ {
+ "location": 0,
+ "name": "position",
+ "type": "vec4"
+ }
+ ],
+ "outputs": [
+ {
+ "location": 0,
+ "name": "v_color",
+ "type": "vec3"
+ }
+ ],
+ "uniformBlocks": [
+ {
+ "binding": 0,
+ "blockName": "buf",
+ "members": [
+ {
+ "matrixStride": 16,
+ "name": "mvp",
+ "offset": 0,
+ "size": 64,
+ "type": "mat4"
+ },
+ {
+ "name": "opacity",
+ "offset": 64,
+ "size": 4,
+ "type": "float"
+ }
+ ],
+ "set": 0,
+ "size": 68,
+ "structName": "ubuf"
+ }
+ ]
+ }
+ \endcode
+
+ The C++ API allows accessing a data structure like the above. For
+ simplicity the inner structs only contain public data members, also
+ considering that their layout is unlikely to change in the future.
+
+ \sa QShaderBaker, QShader
+ */
+
+/*!
+ \enum QShaderDescription::VariableType
+ Represents the type of a variable or block member.
+
+ \value Unknown
+ \value Float
+ \value Vec2
+ \value Vec3
+ \value Vec4
+ \value Mat2
+ \value Mat2x3
+ \value Mat2x4
+ \value Mat3
+ \value Mat3x2
+ \value Mat3x4
+ \value Mat4
+ \value Mat4x2
+ \value Mat4x3
+ \value Int
+ \value Int2
+ \value Int3
+ \value Int4
+ \value Uint
+ \value Uint2
+ \value Uint3
+ \value Uint4
+ \value Bool
+ \value Bool2
+ \value Bool3
+ \value Bool4
+ \value Double
+ \value Double2
+ \value Double3
+ \value Double4
+ \value DMat2
+ \value DMat2x3
+ \value DMat2x4
+ \value DMat3
+ \value DMat3x2
+ \value DMat3x4
+ \value DMat4
+ \value DMat4x2
+ \value DMat4x3
+ \value Sampler1D
+ \value Sampler2D
+ \value Sampler2DMS
+ \value Sampler3D
+ \value SamplerCube
+ \value Sampler1DArray
+ \value Sampler2DArray
+ \value Sampler2DMSArray
+ \value Sampler3DArray
+ \value SamplerCubeArray
+ \value SamplerRect
+ \value SamplerBuffer
+ \value Image1D
+ \value Image2D
+ \value Image2DMS
+ \value Image3D
+ \value ImageCube
+ \value Image1DArray
+ \value Image2DArray
+ \value Image2DMSArray
+ \value Image3DArray
+ \value ImageCubeArray
+ \value ImageRect
+ \value ImageBuffer
+ \value Struct
+ */
+
+/*!
+ \class QShaderDescription::InOutVariable
+ \inmodule QtRhi
+
+ \brief Describes an input or output variable in the shader.
+ */
+
+/*!
+ \class QShaderDescription::BlockVariable
+ \inmodule QtRhi
+
+ \brief Describes a member of a uniform or push constant block.
+ */
+
+/*!
+ \class QShaderDescription::UniformBlock
+ \inmodule QtRhi
+
+ \brief Describes a uniform block.
+
+ \note When translating to shading languages without uniform block support
+ (like GLSL 120 or GLSL/ES 100), uniform blocks are replaced with ordinary
+ uniforms in a struct. The name of the struct, and so the prefix for the
+ uniforms generated from the block members, is given by structName.
+ */
+
+/*!
+ \class QShaderDescription::PushConstantBlock
+ \inmodule QtRhi
+
+ \brief Describes a push constant block.
+ */
+
+/*!
+ \class QShaderDescription::StorageBlock
+ \inmodule QtRhi
+
+ \brief Describes a shader storage block.
+ */
+
+/*!
+ Constructs a new, empty QShaderDescription.
+
+ \note Being empty implies that isValid() returns \c false for the
+ newly constructed instance.
+ */
+QShaderDescription::QShaderDescription()
+ : d(new QShaderDescriptionPrivate)
+{
+}
+
+/*!
+ \internal
+ */
+void QShaderDescription::detach()
+{
+ qAtomicDetach(d);
+}
+
+/*!
+ \internal
+ */
+QShaderDescription::QShaderDescription(const QShaderDescription &other)
+ : d(other.d)
+{
+ d->ref.ref();
+}
+
+/*!
+ \internal
+ */
+QShaderDescription &QShaderDescription::operator=(const QShaderDescription &other)
+{
+ qAtomicAssign(d, other.d);
+ return *this;
+}
+
+/*!
+ Destructor.
+ */
+QShaderDescription::~QShaderDescription()
+{
+ if (!d->ref.deref())
+ delete d;
+}
+
+/*!
+ \return true if the QShaderDescription contains at least one entry in one of
+ the variable and block lists.
+ */
+bool QShaderDescription::isValid() const
+{
+ return !d->inVars.isEmpty() || !d->outVars.isEmpty()
+ || !d->uniformBlocks.isEmpty() || !d->pushConstantBlocks.isEmpty() || !d->storageBlocks.isEmpty()
+ || !d->combinedImageSamplers.isEmpty() || !d->storageImages.isEmpty();
+}
+
+/*!
+ \return a serialized binary version of the data.
+
+ \sa toJson()
+ */
+QByteArray QShaderDescription::toBinaryJson() const
+{
+ return d->makeDoc().toBinaryData();
+}
+
+/*!
+ \return a serialized JSON text version of the data.
+
+ \note There is no deserialization method provided for JSON text.
+
+ \sa toBinaryJson()
+ */
+QByteArray QShaderDescription::toJson() const
+{
+ return d->makeDoc().toJson();
+}
+
+/*!
+ Deserializes the given binary JSON \a data and returns a new
+ QShaderDescription.
+ */
+QShaderDescription QShaderDescription::fromBinaryJson(const QByteArray &data)
+{
+ QShaderDescription desc;
+ QShaderDescriptionPrivate::get(&desc)->loadDoc(QJsonDocument::fromBinaryData(data));
+ return desc;
+}
+
+/*!
+ \return the list of input variables. This includes vertex inputs (sometimes
+ called attributes) for the vertex stage, and inputs for other stages
+ (sometimes called varyings).
+ */
+QVector<QShaderDescription::InOutVariable> QShaderDescription::inputVariables() const
+{
+ return d->inVars;
+}
+
+/*!
+ \return the list of output variables.
+ */
+QVector<QShaderDescription::InOutVariable> QShaderDescription::outputVariables() const
+{
+ return d->outVars;
+}
+
+/*!
+ \return the list of uniform blocks.
+ */
+QVector<QShaderDescription::UniformBlock> QShaderDescription::uniformBlocks() const
+{
+ return d->uniformBlocks;
+}
+
+/*!
+ \return the list of push constant blocks.
+
+ \note Avoid relying on push constant blocks for shaders that are to be used
+ in combination with the Qt Rendering Hardware Interface since that
+ currently has no support for them.
+ */
+QVector<QShaderDescription::PushConstantBlock> QShaderDescription::pushConstantBlocks() const
+{
+ return d->pushConstantBlocks;
+}
+
+/*!
+ \return the list of shader storage blocks.
+
+ For example, with GLSL/Vulkan shaders as source, the declaration
+
+ \badcode
+ struct Stuff {
+ vec2 a;
+ vec2 b;
+ };
+ layout(std140, binding = 0) buffer StuffSsbo {
+ vec4 whatever;
+ Stuff stuff[];
+ } buf;
+ \endcode
+
+ generates the following: (shown as textual JSON here)
+
+ \badcode
+ "storageBlocks": [ {
+ "binding": 0,
+ "blockName": "StuffSsbo",
+ "instanceName": "buf",
+ "knownSize": 16,
+ "members": [
+ {
+ "name": "whatever",
+ "offset": 0,
+ "size": 16,
+ "type": "vec4"
+ },
+ {
+ "arrayDims": [
+ 0
+ ],
+ "name": "stuff",
+ "offset": 16,
+ "size": 0,
+ "structMembers": [
+ {
+ "name": "a",
+ "offset": 0,
+ "size": 8,
+ "type": "vec2"
+ },
+ {
+ "name": "b",
+ "offset": 8,
+ "size": 8,
+ "type": "vec2"
+ }
+ ],
+ "type": "struct"
+ }
+ ],
+ "set": 0
+ } ]
+ \endcode
+
+ \note The size of the last member in the storage block is undefined. This shows
+ up as \c size 0 and an array dimension of \c{[0]}. The storage block's \c knownSize
+ excludes the size of the last member since that will only be known at run time.
+
+ \note SSBOs are not available with some graphics APIs, such as, OpenGL 2.x or
+ OpenGL ES older than 3.1.
+ */
+QVector<QShaderDescription::StorageBlock> QShaderDescription::storageBlocks() const
+{
+ return d->storageBlocks;
+}
+
+/*!
+ \return the list of combined image samplers
+
+ With GLSL/Vulkan shaders as source a \c{layout(binding = 1) uniform sampler2D tex;}
+ uniform generates the following: (shown as textual JSON here)
+
+ \badcode
+ "combinedImageSamplers": [
+ {
+ "binding": 1,
+ "name": "tex",
+ "set": 0,
+ "type": "sampler2D"
+ }
+ ]
+ \endcode
+
+ This does not mean that other language versions of the shader must also use
+ a combined image sampler, especially considering that the concept may not
+ exist everywhere. For instance, a HLSL version will likely just use a
+ Texture2D and SamplerState object with registers t1 and s1, respectively.
+ */
+QVector<QShaderDescription::InOutVariable> QShaderDescription::combinedImageSamplers() const
+{
+ return d->combinedImageSamplers;
+}
+
+/*!
+ \return the list of image variables.
+
+ These will likely occur in compute shaders. For example,
+ \c{layout (binding = 0, rgba8) uniform readonly image2D inputImage;}
+ generates the following: (shown as textual JSON here)
+
+ \badcode
+ "storageImages": [
+ {
+ "binding": 0,
+ "imageFormat": "rgba8",
+ "name": "inputImage",
+ "set": 0,
+ "type": "image2D"
+ }
+ ]
+ \endcode
+
+ \note Separate image objects are not compatible with some graphics APIs,
+ such as, OpenGL 2.x or OpenGL ES older than 3.1.
+ */
+QVector<QShaderDescription::InOutVariable> QShaderDescription::storageImages() const
+{
+ return d->storageImages;
+}
+
+static struct TypeTab {
+ QString k;
+ QShaderDescription::VariableType v;
+} typeTab[] = {
+ { QLatin1String("float"), QShaderDescription::Float },
+ { QLatin1String("vec2"), QShaderDescription::Vec2 },
+ { QLatin1String("vec3"), QShaderDescription::Vec3 },
+ { QLatin1String("vec4"), QShaderDescription::Vec4 },
+ { QLatin1String("mat2"), QShaderDescription::Mat2 },
+ { QLatin1String("mat3"), QShaderDescription::Mat3 },
+ { QLatin1String("mat4"), QShaderDescription::Mat4 },
+
+ { QLatin1String("struct"), QShaderDescription::Struct },
+
+ { QLatin1String("sampler1D"), QShaderDescription::Sampler1D },
+ { QLatin1String("sampler2D"), QShaderDescription::Sampler2D },
+ { QLatin1String("sampler2DMS"), QShaderDescription::Sampler2DMS },
+ { QLatin1String("sampler3D"), QShaderDescription::Sampler3D },
+ { QLatin1String("samplerCube"), QShaderDescription::SamplerCube },
+ { QLatin1String("sampler1DArray"), QShaderDescription::Sampler1DArray },
+ { QLatin1String("sampler2DArray"), QShaderDescription::Sampler2DArray },
+ { QLatin1String("sampler2DMSArray"), QShaderDescription::Sampler2DMSArray },
+ { QLatin1String("sampler3DArray"), QShaderDescription::Sampler3DArray },
+ { QLatin1String("samplerCubeArray"), QShaderDescription::SamplerCubeArray },
+ { QLatin1String("samplerRect"), QShaderDescription::SamplerRect },
+ { QLatin1String("samplerBuffer"), QShaderDescription::SamplerBuffer },
+
+ { QLatin1String("mat2x3"), QShaderDescription::Mat2x3 },
+ { QLatin1String("mat2x4"), QShaderDescription::Mat2x4 },
+ { QLatin1String("mat3x2"), QShaderDescription::Mat3x2 },
+ { QLatin1String("mat3x4"), QShaderDescription::Mat3x4 },
+ { QLatin1String("mat4x2"), QShaderDescription::Mat4x2 },
+ { QLatin1String("mat4x3"), QShaderDescription::Mat4x3 },
+
+ { QLatin1String("int"), QShaderDescription::Int },
+ { QLatin1String("ivec2"), QShaderDescription::Int2 },
+ { QLatin1String("ivec3"), QShaderDescription::Int3 },
+ { QLatin1String("ivec4"), QShaderDescription::Int4 },
+
+ { QLatin1String("uint"), QShaderDescription::Uint },
+ { QLatin1String("uvec2"), QShaderDescription::Uint2 },
+ { QLatin1String("uvec3"), QShaderDescription::Uint3 },
+ { QLatin1String("uvec4"), QShaderDescription::Uint4 },
+
+ { QLatin1String("bool"), QShaderDescription::Bool },
+ { QLatin1String("bvec2"), QShaderDescription::Bool2 },
+ { QLatin1String("bvec3"), QShaderDescription::Bool3 },
+ { QLatin1String("bvec4"), QShaderDescription::Bool4 },
+
+ { QLatin1String("double"), QShaderDescription::Double },
+ { QLatin1String("dvec2"), QShaderDescription::Double2 },
+ { QLatin1String("dvec3"), QShaderDescription::Double3 },
+ { QLatin1String("dvec4"), QShaderDescription::Double4 },
+ { QLatin1String("dmat2"), QShaderDescription::DMat2 },
+ { QLatin1String("dmat3"), QShaderDescription::DMat3 },
+ { QLatin1String("dmat4"), QShaderDescription::DMat4 },
+ { QLatin1String("dmat2x3"), QShaderDescription::DMat2x3 },
+ { QLatin1String("dmat2x4"), QShaderDescription::DMat2x4 },
+ { QLatin1String("dmat3x2"), QShaderDescription::DMat3x2 },
+ { QLatin1String("dmat3x4"), QShaderDescription::DMat3x4 },
+ { QLatin1String("dmat4x2"), QShaderDescription::DMat4x2 },
+ { QLatin1String("dmat4x3"), QShaderDescription::DMat4x3 },
+
+ { QLatin1String("image1D"), QShaderDescription::Image1D },
+ { QLatin1String("image2D"), QShaderDescription::Image2D },
+ { QLatin1String("image2DMS"), QShaderDescription::Image2DMS },
+ { QLatin1String("image3D"), QShaderDescription::Image3D },
+ { QLatin1String("imageCube"), QShaderDescription::ImageCube },
+ { QLatin1String("image1DArray"), QShaderDescription::Image1DArray },
+ { QLatin1String("image2DArray"), QShaderDescription::Image2DArray },
+ { QLatin1String("image2DMSArray"), QShaderDescription::Image2DMSArray },
+ { QLatin1String("image3DArray"), QShaderDescription::Image3DArray },
+ { QLatin1String("imageCubeArray"), QShaderDescription::ImageCubeArray },
+ { QLatin1String("imageRect"), QShaderDescription::ImageRect },
+ { QLatin1String("imageBuffer"), QShaderDescription::ImageBuffer }
+};
+
+static QString typeStr(const QShaderDescription::VariableType &t)
+{
+ for (size_t i = 0; i < sizeof(typeTab) / sizeof(TypeTab); ++i) {
+ if (typeTab[i].v == t)
+ return typeTab[i].k;
+ }
+ return QString();
+}
+
+static QShaderDescription::VariableType mapType(const QString &t)
+{
+ for (size_t i = 0; i < sizeof(typeTab) / sizeof(TypeTab); ++i) {
+ if (typeTab[i].k == t)
+ return typeTab[i].v;
+ }
+ return QShaderDescription::Unknown;
+}
+
+static struct ImageFormatTab {
+ QString k;
+ QShaderDescription::ImageFormat v;
+} imageFormatTab[] {
+ { QLatin1String("unknown"), QShaderDescription::ImageFormatUnknown },
+ { QLatin1String("rgba32f"), QShaderDescription::ImageFormatRgba32f },
+ { QLatin1String("rgba16"), QShaderDescription::ImageFormatRgba16f },
+ { QLatin1String("r32f"), QShaderDescription::ImageFormatR32f },
+ { QLatin1String("rgba8"), QShaderDescription::ImageFormatRgba8 },
+ { QLatin1String("rgba8_snorm"), QShaderDescription::ImageFormatRgba8Snorm },
+ { QLatin1String("rg32f"), QShaderDescription::ImageFormatRg32f },
+ { QLatin1String("rg16f"), QShaderDescription::ImageFormatRg16f },
+ { QLatin1String("r11f_g11f_b10f"), QShaderDescription::ImageFormatR11fG11fB10f },
+ { QLatin1String("r16f"), QShaderDescription::ImageFormatR16f },
+ { QLatin1String("rgba16"), QShaderDescription::ImageFormatRgba16 },
+ { QLatin1String("rgb10_a2"), QShaderDescription::ImageFormatRgb10A2 },
+ { QLatin1String("rg16"), QShaderDescription::ImageFormatRg16 },
+ { QLatin1String("rg8"), QShaderDescription::ImageFormatRg8 },
+ { QLatin1String("r16"), QShaderDescription::ImageFormatR16 },
+ { QLatin1String("r8"), QShaderDescription::ImageFormatR8 },
+ { QLatin1String("rgba16_snorm"), QShaderDescription::ImageFormatRgba16Snorm },
+ { QLatin1String("rg16_snorm"), QShaderDescription::ImageFormatRg16Snorm },
+ { QLatin1String("rg8_snorm"), QShaderDescription::ImageFormatRg8Snorm },
+ { QLatin1String("r16_snorm"), QShaderDescription::ImageFormatR16Snorm },
+ { QLatin1String("r8_snorm"), QShaderDescription::ImageFormatR8Snorm },
+ { QLatin1String("rgba32i"), QShaderDescription::ImageFormatRgba32i },
+ { QLatin1String("rgba16i"), QShaderDescription::ImageFormatRgba16i },
+ { QLatin1String("rgba8i"), QShaderDescription::ImageFormatRgba8i },
+ { QLatin1String("r32i"), QShaderDescription::ImageFormatR32i },
+ { QLatin1String("rg32i"), QShaderDescription::ImageFormatRg32i },
+ { QLatin1String("rg16i"), QShaderDescription::ImageFormatRg16i },
+ { QLatin1String("rg8i"), QShaderDescription::ImageFormatRg8i },
+ { QLatin1String("r16i"), QShaderDescription::ImageFormatR16i },
+ { QLatin1String("r8i"), QShaderDescription::ImageFormatR8i },
+ { QLatin1String("rgba32ui"), QShaderDescription::ImageFormatRgba32ui },
+ { QLatin1String("rgba16ui"), QShaderDescription::ImageFormatRgba16ui },
+ { QLatin1String("rgba8ui"), QShaderDescription::ImageFormatRgba8ui },
+ { QLatin1String("r32ui"), QShaderDescription::ImageFormatR32ui },
+ { QLatin1String("rgb10_a2ui"), QShaderDescription::ImageFormatRgb10a2ui },
+ { QLatin1String("rg32ui"), QShaderDescription::ImageFormatRg32ui },
+ { QLatin1String("rg16ui"), QShaderDescription::ImageFormatRg16ui },
+ { QLatin1String("rg8ui"), QShaderDescription::ImageFormatRg8ui },
+ { QLatin1String("r16ui"), QShaderDescription::ImageFormatR16ui },
+ { QLatin1String("r8ui"), QShaderDescription::ImageFormatR8ui }
+};
+
+static QString imageFormatStr(const QShaderDescription::ImageFormat &f)
+{
+ for (size_t i = 0; i < sizeof(imageFormatTab) / sizeof(ImageFormatTab); ++i) {
+ if (imageFormatTab[i].v == f)
+ return imageFormatTab[i].k;
+ }
+ return QString();
+}
+
+static QShaderDescription::ImageFormat mapImageFormat(const QString &f)
+{
+ for (size_t i = 0; i < sizeof(imageFormatTab) / sizeof(ImageFormatTab); ++i) {
+ if (imageFormatTab[i].k == f)
+ return imageFormatTab[i].v;
+ }
+ return QShaderDescription::ImageFormatUnknown;
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug dbg, const QShaderDescription &sd)
+{
+ const QShaderDescriptionPrivate *d = sd.d;
+ QDebugStateSaver saver(dbg);
+
+ if (sd.isValid()) {
+ dbg.nospace() << "QShaderDescription("
+ << "inVars " << d->inVars
+ << " outVars " << d->outVars
+ << " uniformBlocks " << d->uniformBlocks
+ << " pcBlocks " << d->pushConstantBlocks
+ << " storageBlocks " << d->storageBlocks
+ << " combinedSamplers " << d->combinedImageSamplers
+ << " images " << d->storageImages
+ << ')';
+ } else {
+ dbg.nospace() << "QShaderDescription(null)";
+ }
+
+ return dbg;
+}
+
+QDebug operator<<(QDebug dbg, const QShaderDescription::InOutVariable &var)
+{
+ QDebugStateSaver saver(dbg);
+ dbg.nospace() << "InOutVariable(" << typeStr(var.type) << ' ' << var.name;
+ if (var.location >= 0)
+ dbg.nospace() << " location=" << var.location;
+ if (var.binding >= 0)
+ dbg.nospace() << " binding=" << var.binding;
+ if (var.descriptorSet >= 0)
+ dbg.nospace() << " set=" << var.descriptorSet;
+ if (var.imageFormat != QShaderDescription::ImageFormatUnknown)
+ dbg.nospace() << " imageFormat=" << imageFormatStr(var.imageFormat);
+ if (var.imageFlags)
+ dbg.nospace() << " imageFlags=" << var.imageFlags;
+ dbg.nospace() << ')';
+ return dbg;
+}
+
+QDebug operator<<(QDebug dbg, const QShaderDescription::BlockVariable &var)
+{
+ QDebugStateSaver saver(dbg);
+ dbg.nospace() << "BlockVariable(" << typeStr(var.type) << ' ' << var.name
+ << " offset=" << var.offset << " size=" << var.size;
+ if (!var.arrayDims.isEmpty())
+ dbg.nospace() << " array=" << var.arrayDims;
+ if (var.arrayStride)
+ dbg.nospace() << " arrayStride=" << var.arrayStride;
+ if (var.matrixStride)
+ dbg.nospace() << " matrixStride=" << var.matrixStride;
+ if (var.matrixIsRowMajor)
+ dbg.nospace() << " [rowmaj]";
+ if (!var.structMembers.isEmpty())
+ dbg.nospace() << " structMembers=" << var.structMembers;
+ dbg.nospace() << ')';
+ return dbg;
+}
+
+QDebug operator<<(QDebug dbg, const QShaderDescription::UniformBlock &blk)
+{
+ QDebugStateSaver saver(dbg);
+ dbg.nospace() << "UniformBlock(" << blk.blockName << ' ' << blk.structName << " size=" << blk.size;
+ if (blk.binding >= 0)
+ dbg.nospace() << " binding=" << blk.binding;
+ if (blk.descriptorSet >= 0)
+ dbg.nospace() << " set=" << blk.descriptorSet;
+ dbg.nospace() << ' ' << blk.members << ')';
+ return dbg;
+}
+
+QDebug operator<<(QDebug dbg, const QShaderDescription::PushConstantBlock &blk)
+{
+ QDebugStateSaver saver(dbg);
+ dbg.nospace() << "PushConstantBlock(" << blk.name << " size=" << blk.size << ' ' << blk.members << ')';
+ return dbg;
+}
+
+QDebug operator<<(QDebug dbg, const QShaderDescription::StorageBlock &blk)
+{
+ QDebugStateSaver saver(dbg);
+ dbg.nospace() << "StorageBlock(" << blk.blockName << ' ' << blk.instanceName << " knownSize=" << blk.knownSize;
+ if (blk.binding >= 0)
+ dbg.nospace() << " binding=" << blk.binding;
+ if (blk.descriptorSet >= 0)
+ dbg.nospace() << " set=" << blk.descriptorSet;
+ dbg.nospace() << ' ' << blk.members << ')';
+ return dbg;
+}
+#endif
+
+static const QString nameKey = QLatin1String("name");
+static const QString typeKey = QLatin1String("type");
+static const QString locationKey = QLatin1String("location");
+static const QString bindingKey = QLatin1String("binding");
+static const QString setKey = QLatin1String("set");
+static const QString imageFormatKey = QLatin1String("imageFormat");
+static const QString imageFlagsKey = QLatin1String("imageFlags");
+static const QString offsetKey = QLatin1String("offset");
+static const QString arrayDimsKey = QLatin1String("arrayDims");
+static const QString arrayStrideKey = QLatin1String("arrayStride");
+static const QString matrixStrideKey = QLatin1String("matrixStride");
+static const QString matrixRowMajorKey = QLatin1String("matrixRowMajor");
+static const QString structMembersKey = QLatin1String("structMembers");
+static const QString membersKey = QLatin1String("members");
+static const QString inputsKey = QLatin1String("inputs");
+static const QString outputsKey = QLatin1String("outputs");
+static const QString uniformBlocksKey = QLatin1String("uniformBlocks");
+static const QString blockNameKey = QLatin1String("blockName");
+static const QString structNameKey = QLatin1String("structName");
+static const QString instanceNameKey = QLatin1String("instanceName");
+static const QString sizeKey = QLatin1String("size");
+static const QString knownSizeKey = QLatin1String("knownSize");
+static const QString pushConstantBlocksKey = QLatin1String("pushConstantBlocks");
+static const QString storageBlocksKey = QLatin1String("storageBlocks");
+static const QString combinedImageSamplersKey = QLatin1String("combinedImageSamplers");
+static const QString storageImagesKey = QLatin1String("storageImages");
+
+static void addDeco(QJsonObject *obj, const QShaderDescription::InOutVariable &v)
+{
+ if (v.location >= 0)
+ (*obj)[locationKey] = v.location;
+ if (v.binding >= 0)
+ (*obj)[bindingKey] = v.binding;
+ if (v.descriptorSet >= 0)
+ (*obj)[setKey] = v.descriptorSet;
+ if (v.imageFormat != QShaderDescription::ImageFormatUnknown)
+ (*obj)[imageFormatKey] = imageFormatStr(v.imageFormat);
+ if (v.imageFlags)
+ (*obj)[imageFlagsKey] = int(v.imageFlags);
+}
+
+static QJsonObject inOutObject(const QShaderDescription::InOutVariable &v)
+{
+ QJsonObject obj;
+ obj[nameKey] = v.name;
+ obj[typeKey] = typeStr(v.type);
+ addDeco(&obj, v);
+ return obj;
+}
+
+static QJsonObject blockMemberObject(const QShaderDescription::BlockVariable &v)
+{
+ QJsonObject obj;
+ obj[nameKey] = v.name;
+ obj[typeKey] = typeStr(v.type);
+ obj[offsetKey] = v.offset;
+ obj[sizeKey] = v.size;
+ if (!v.arrayDims.isEmpty()) {
+ QJsonArray dimArr;
+ for (int dim : v.arrayDims)
+ dimArr.append(dim);
+ obj[arrayDimsKey] = dimArr;
+ }
+ if (v.arrayStride)
+ obj[arrayStrideKey] = v.arrayStride;
+ if (v.matrixStride)
+ obj[matrixStrideKey] = v.matrixStride;
+ if (v.matrixIsRowMajor)
+ obj[matrixRowMajorKey] = true;
+ if (!v.structMembers.isEmpty()) {
+ QJsonArray arr;
+ for (const QShaderDescription::BlockVariable &sv : v.structMembers)
+ arr.append(blockMemberObject(sv));
+ obj[structMembersKey] = arr;
+ }
+ return obj;
+}
+
+QJsonDocument QShaderDescriptionPrivate::makeDoc()
+{
+ QJsonObject root;
+
+ QJsonArray jinputs;
+ for (const QShaderDescription::InOutVariable &v : qAsConst(inVars))
+ jinputs.append(inOutObject(v));
+ if (!jinputs.isEmpty())
+ root[inputsKey] = jinputs;
+
+ QJsonArray joutputs;
+ for (const QShaderDescription::InOutVariable &v : qAsConst(outVars))
+ joutputs.append(inOutObject(v));
+ if (!joutputs.isEmpty())
+ root[outputsKey] = joutputs;
+
+ QJsonArray juniformBlocks;
+ for (const QShaderDescription::UniformBlock &b : uniformBlocks) {
+ QJsonObject juniformBlock;
+ juniformBlock[blockNameKey] = b.blockName;
+ juniformBlock[structNameKey] = b.structName;
+ juniformBlock[sizeKey] = b.size;
+ if (b.binding >= 0)
+ juniformBlock[bindingKey] = b.binding;
+ if (b.descriptorSet >= 0)
+ juniformBlock[setKey] = b.descriptorSet;
+ QJsonArray members;
+ for (const QShaderDescription::BlockVariable &v : b.members)
+ members.append(blockMemberObject(v));
+ juniformBlock[membersKey] = members;
+ juniformBlocks.append(juniformBlock);
+ }
+ if (!juniformBlocks.isEmpty())
+ root[uniformBlocksKey] = juniformBlocks;
+
+ QJsonArray jpushConstantBlocks;
+ for (const QShaderDescription::PushConstantBlock &b : pushConstantBlocks) {
+ QJsonObject jpushConstantBlock;
+ jpushConstantBlock[nameKey] = b.name;
+ jpushConstantBlock[sizeKey] = b.size;
+ QJsonArray members;
+ for (const QShaderDescription::BlockVariable &v : b.members)
+ members.append(blockMemberObject(v));
+ jpushConstantBlock[membersKey] = members;
+ jpushConstantBlocks.append(jpushConstantBlock);
+ }
+ if (!jpushConstantBlocks.isEmpty())
+ root[pushConstantBlocksKey] = jpushConstantBlocks;
+
+ QJsonArray jstorageBlocks;
+ for (const QShaderDescription::StorageBlock &b : storageBlocks) {
+ QJsonObject jstorageBlock;
+ jstorageBlock[blockNameKey] = b.blockName;
+ jstorageBlock[instanceNameKey] = b.instanceName;
+ jstorageBlock[knownSizeKey] = b.knownSize;
+ if (b.binding >= 0)
+ jstorageBlock[bindingKey] = b.binding;
+ if (b.descriptorSet >= 0)
+ jstorageBlock[setKey] = b.descriptorSet;
+ QJsonArray members;
+ for (const QShaderDescription::BlockVariable &v : b.members)
+ members.append(blockMemberObject(v));
+ jstorageBlock[membersKey] = members;
+ jstorageBlocks.append(jstorageBlock);
+ }
+ if (!jstorageBlocks.isEmpty())
+ root[storageBlocksKey] = jstorageBlocks;
+
+ QJsonArray jcombinedSamplers;
+ for (const QShaderDescription::InOutVariable &v : qAsConst(combinedImageSamplers)) {
+ QJsonObject sampler;
+ sampler[nameKey] = v.name;
+ sampler[typeKey] = typeStr(v.type);
+ addDeco(&sampler, v);
+ jcombinedSamplers.append(sampler);
+ }
+ if (!jcombinedSamplers.isEmpty())
+ root[combinedImageSamplersKey] = jcombinedSamplers;
+
+ QJsonArray jstorageImages;
+ for (const QShaderDescription::InOutVariable &v : qAsConst(storageImages)) {
+ QJsonObject image;
+ image[nameKey] = v.name;
+ image[typeKey] = typeStr(v.type);
+ addDeco(&image, v);
+ jstorageImages.append(image);
+ }
+ if (!jstorageImages.isEmpty())
+ root[storageImagesKey] = jstorageImages;
+
+ return QJsonDocument(root);
+}
+
+static QShaderDescription::InOutVariable inOutVar(const QJsonObject &obj)
+{
+ QShaderDescription::InOutVariable var;
+ var.name = obj[nameKey].toString();
+ var.type = mapType(obj[typeKey].toString());
+ if (obj.contains(locationKey))
+ var.location = obj[locationKey].toInt();
+ if (obj.contains(bindingKey))
+ var.binding = obj[bindingKey].toInt();
+ if (obj.contains(setKey))
+ var.descriptorSet = obj[setKey].toInt();
+ if (obj.contains(imageFormatKey))
+ var.imageFormat = mapImageFormat(obj[imageFormatKey].toString());
+ if (obj.contains(imageFlagsKey))
+ var.imageFlags = QShaderDescription::ImageFlags(obj[imageFlagsKey].toInt());
+ return var;
+}
+
+static QShaderDescription::BlockVariable blockVar(const QJsonObject &obj)
+{
+ QShaderDescription::BlockVariable var;
+ var.name = obj[nameKey].toString();
+ var.type = mapType(obj[typeKey].toString());
+ var.offset = obj[offsetKey].toInt();
+ var.size = obj[sizeKey].toInt();
+ if (obj.contains(arrayDimsKey)) {
+ QJsonArray dimArr = obj[arrayDimsKey].toArray();
+ for (int i = 0; i < dimArr.count(); ++i)
+ var.arrayDims.append(dimArr.at(i).toInt());
+ }
+ if (obj.contains(arrayStrideKey))
+ var.arrayStride = obj[arrayStrideKey].toInt();
+ if (obj.contains(matrixStrideKey))
+ var.matrixStride = obj[matrixStrideKey].toInt();
+ if (obj.contains(matrixRowMajorKey))
+ var.matrixIsRowMajor = obj[matrixRowMajorKey].toBool();
+ if (obj.contains(structMembersKey)) {
+ QJsonArray arr = obj[structMembersKey].toArray();
+ for (int i = 0; i < arr.count(); ++i)
+ var.structMembers.append(blockVar(arr.at(i).toObject()));
+ }
+ return var;
+}
+
+void QShaderDescriptionPrivate::loadDoc(const QJsonDocument &doc)
+{
+ if (doc.isNull()) {
+ qWarning("QShaderDescription: JSON document is empty");
+ return;
+ }
+
+ Q_ASSERT(ref.load() == 1); // must be detached
+
+ inVars.clear();
+ outVars.clear();
+ uniformBlocks.clear();
+ pushConstantBlocks.clear();
+ storageBlocks.clear();
+ combinedImageSamplers.clear();
+ storageImages.clear();
+
+ QJsonObject root = doc.object();
+
+ if (root.contains(inputsKey)) {
+ QJsonArray inputs = root[inputsKey].toArray();
+ for (int i = 0; i < inputs.count(); ++i)
+ inVars.append(inOutVar(inputs[i].toObject()));
+ }
+
+ if (root.contains(outputsKey)) {
+ QJsonArray outputs = root[outputsKey].toArray();
+ for (int i = 0; i < outputs.count(); ++i)
+ outVars.append(inOutVar(outputs[i].toObject()));
+ }
+
+ if (root.contains(uniformBlocksKey)) {
+ QJsonArray ubs = root[uniformBlocksKey].toArray();
+ for (int i = 0; i < ubs.count(); ++i) {
+ QJsonObject ubObj = ubs[i].toObject();
+ QShaderDescription::UniformBlock ub;
+ ub.blockName = ubObj[blockNameKey].toString();
+ ub.structName = ubObj[structNameKey].toString();
+ ub.size = ubObj[sizeKey].toInt();
+ if (ubObj.contains(bindingKey))
+ ub.binding = ubObj[bindingKey].toInt();
+ if (ubObj.contains(setKey))
+ ub.descriptorSet = ubObj[setKey].toInt();
+ QJsonArray members = ubObj[membersKey].toArray();
+ for (const QJsonValue &member : members)
+ ub.members.append(blockVar(member.toObject()));
+ uniformBlocks.append(ub);
+ }
+ }
+
+ if (root.contains(pushConstantBlocksKey)) {
+ QJsonArray pcs = root[pushConstantBlocksKey].toArray();
+ for (int i = 0; i < pcs.count(); ++i) {
+ QJsonObject pcObj = pcs[i].toObject();
+ QShaderDescription::PushConstantBlock pc;
+ pc.name = pcObj[nameKey].toString();
+ pc.size = pcObj[sizeKey].toInt();
+ QJsonArray members = pcObj[membersKey].toArray();
+ for (const QJsonValue &member : members)
+ pc.members.append(blockVar(member.toObject()));
+ pushConstantBlocks.append(pc);
+ }
+ }
+
+ if (root.contains(storageBlocksKey)) {
+ QJsonArray ubs = root[storageBlocksKey].toArray();
+ for (int i = 0; i < ubs.count(); ++i) {
+ QJsonObject sbObj = ubs[i].toObject();
+ QShaderDescription::StorageBlock sb;
+ sb.blockName = sbObj[blockNameKey].toString();
+ sb.instanceName = sbObj[instanceNameKey].toString();
+ sb.knownSize = sbObj[knownSizeKey].toInt();
+ if (sbObj.contains(bindingKey))
+ sb.binding = sbObj[bindingKey].toInt();
+ if (sbObj.contains(setKey))
+ sb.descriptorSet = sbObj[setKey].toInt();
+ QJsonArray members = sbObj[membersKey].toArray();
+ for (const QJsonValue &member : members)
+ sb.members.append(blockVar(member.toObject()));
+ storageBlocks.append(sb);
+ }
+ }
+
+ if (root.contains(combinedImageSamplersKey)) {
+ QJsonArray samplers = root[combinedImageSamplersKey].toArray();
+ for (int i = 0; i < samplers.count(); ++i)
+ combinedImageSamplers.append(inOutVar(samplers[i].toObject()));
+ }
+
+ if (root.contains(storageImagesKey)) {
+ QJsonArray images = root[storageImagesKey].toArray();
+ for (int i = 0; i < images.count(); ++i)
+ storageImages.append(inOutVar(images[i].toObject()));
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/rhi/qshaderdescription_p.h b/src/gui/rhi/qshaderdescription_p.h
new file mode 100644
index 0000000000..43d4256a63
--- /dev/null
+++ b/src/gui/rhi/qshaderdescription_p.h
@@ -0,0 +1,278 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gui module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSHADERDESCRIPTION_H
+#define QSHADERDESCRIPTION_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of a number of Qt sources files. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtGui/qtguiglobal.h>
+#include <QtCore/QString>
+#include <QtCore/QVector>
+
+QT_BEGIN_NAMESPACE
+
+struct QShaderDescriptionPrivate;
+
+class Q_GUI_EXPORT QShaderDescription
+{
+public:
+ QShaderDescription();
+ QShaderDescription(const QShaderDescription &other);
+ QShaderDescription &operator=(const QShaderDescription &other);
+ ~QShaderDescription();
+ void detach();
+
+ bool isValid() const;
+
+ QByteArray toBinaryJson() const;
+ QByteArray toJson() const;
+
+ static QShaderDescription fromBinaryJson(const QByteArray &data);
+
+ enum VariableType {
+ Unknown = 0,
+
+ // do not reorder
+ Float,
+ Vec2,
+ Vec3,
+ Vec4,
+ Mat2,
+ Mat2x3,
+ Mat2x4,
+ Mat3,
+ Mat3x2,
+ Mat3x4,
+ Mat4,
+ Mat4x2,
+ Mat4x3,
+
+ Int,
+ Int2,
+ Int3,
+ Int4,
+
+ Uint,
+ Uint2,
+ Uint3,
+ Uint4,
+
+ Bool,
+ Bool2,
+ Bool3,
+ Bool4,
+
+ Double,
+ Double2,
+ Double3,
+ Double4,
+ DMat2,
+ DMat2x3,
+ DMat2x4,
+ DMat3,
+ DMat3x2,
+ DMat3x4,
+ DMat4,
+ DMat4x2,
+ DMat4x3,
+
+ Sampler1D,
+ Sampler2D,
+ Sampler2DMS,
+ Sampler3D,
+ SamplerCube,
+ Sampler1DArray,
+ Sampler2DArray,
+ Sampler2DMSArray,
+ Sampler3DArray,
+ SamplerCubeArray,
+ SamplerRect,
+ SamplerBuffer,
+
+ Image1D,
+ Image2D,
+ Image2DMS,
+ Image3D,
+ ImageCube,
+ Image1DArray,
+ Image2DArray,
+ Image2DMSArray,
+ Image3DArray,
+ ImageCubeArray,
+ ImageRect,
+ ImageBuffer,
+
+ Struct
+ };
+
+ enum ImageFormat {
+ // must match SPIR-V's ImageFormat
+ ImageFormatUnknown = 0,
+ ImageFormatRgba32f = 1,
+ ImageFormatRgba16f = 2,
+ ImageFormatR32f = 3,
+ ImageFormatRgba8 = 4,
+ ImageFormatRgba8Snorm = 5,
+ ImageFormatRg32f = 6,
+ ImageFormatRg16f = 7,
+ ImageFormatR11fG11fB10f = 8,
+ ImageFormatR16f = 9,
+ ImageFormatRgba16 = 10,
+ ImageFormatRgb10A2 = 11,
+ ImageFormatRg16 = 12,
+ ImageFormatRg8 = 13,
+ ImageFormatR16 = 14,
+ ImageFormatR8 = 15,
+ ImageFormatRgba16Snorm = 16,
+ ImageFormatRg16Snorm = 17,
+ ImageFormatRg8Snorm = 18,
+ ImageFormatR16Snorm = 19,
+ ImageFormatR8Snorm = 20,
+ ImageFormatRgba32i = 21,
+ ImageFormatRgba16i = 22,
+ ImageFormatRgba8i = 23,
+ ImageFormatR32i = 24,
+ ImageFormatRg32i = 25,
+ ImageFormatRg16i = 26,
+ ImageFormatRg8i = 27,
+ ImageFormatR16i = 28,
+ ImageFormatR8i = 29,
+ ImageFormatRgba32ui = 30,
+ ImageFormatRgba16ui = 31,
+ ImageFormatRgba8ui = 32,
+ ImageFormatR32ui = 33,
+ ImageFormatRgb10a2ui = 34,
+ ImageFormatRg32ui = 35,
+ ImageFormatRg16ui = 36,
+ ImageFormatRg8ui = 37,
+ ImageFormatR16ui = 38,
+ ImageFormatR8ui = 39
+ };
+
+ enum ImageFlag {
+ ReadOnlyImage = 1 << 0,
+ WriteOnlyImage = 1 << 1
+ };
+ Q_DECLARE_FLAGS(ImageFlags, ImageFlag)
+
+ // Optional data (like decorations) usually default to an otherwise invalid value (-1 or 0). This is intentional.
+
+ struct InOutVariable {
+ QString name;
+ VariableType type = Unknown;
+ int location = -1;
+ int binding = -1;
+ int descriptorSet = -1;
+ ImageFormat imageFormat = ImageFormatUnknown;
+ ImageFlags imageFlags;
+ };
+
+ struct BlockVariable {
+ QString name;
+ VariableType type = Unknown;
+ int offset = 0;
+ int size = 0;
+ QVector<int> arrayDims;
+ int arrayStride = 0;
+ int matrixStride = 0;
+ bool matrixIsRowMajor = false;
+ QVector<BlockVariable> structMembers;
+ };
+
+ struct UniformBlock {
+ QString blockName;
+ QString structName; // instanceName
+ int size = 0;
+ int binding = -1;
+ int descriptorSet = -1;
+ QVector<BlockVariable> members;
+ };
+
+ struct PushConstantBlock {
+ QString name;
+ int size = 0;
+ QVector<BlockVariable> members;
+ };
+
+ struct StorageBlock {
+ QString blockName;
+ QString instanceName;
+ int knownSize = 0;
+ int binding = -1;
+ int descriptorSet = -1;
+ QVector<BlockVariable> members;
+ };
+
+ QVector<InOutVariable> inputVariables() const;
+ QVector<InOutVariable> outputVariables() const;
+ QVector<UniformBlock> uniformBlocks() const;
+ QVector<PushConstantBlock> pushConstantBlocks() const;
+ QVector<StorageBlock> storageBlocks() const;
+ QVector<InOutVariable> combinedImageSamplers() const;
+ QVector<InOutVariable> storageImages() const;
+
+private:
+ QShaderDescriptionPrivate *d;
+ friend struct QShaderDescriptionPrivate;
+#ifndef QT_NO_DEBUG_STREAM
+ friend Q_GUI_EXPORT QDebug operator<<(QDebug, const QShaderDescription &);
+#endif
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QShaderDescription::ImageFlags)
+
+#ifndef QT_NO_DEBUG_STREAM
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QShaderDescription &);
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QShaderDescription::InOutVariable &);
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QShaderDescription::BlockVariable &);
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QShaderDescription::UniformBlock &);
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QShaderDescription::PushConstantBlock &);
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QShaderDescription::StorageBlock &);
+#endif
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/rhi/qshaderdescription_p_p.h b/src/gui/rhi/qshaderdescription_p_p.h
new file mode 100644
index 0000000000..dbe68d1060
--- /dev/null
+++ b/src/gui/rhi/qshaderdescription_p_p.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gui module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSHADERDESCRIPTION_P_H
+#define QSHADERDESCRIPTION_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of a number of Qt sources files. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qshaderdescription_p.h"
+#include <QtCore/QVector>
+#include <QtCore/QAtomicInt>
+#include <QtCore/QJsonDocument>
+
+QT_BEGIN_NAMESPACE
+
+struct Q_GUI_EXPORT QShaderDescriptionPrivate
+{
+ QShaderDescriptionPrivate()
+ : ref(1)
+ {
+ }
+
+ QShaderDescriptionPrivate(const QShaderDescriptionPrivate *other)
+ : ref(1),
+ inVars(other->inVars),
+ outVars(other->outVars),
+ uniformBlocks(other->uniformBlocks),
+ pushConstantBlocks(other->pushConstantBlocks),
+ storageBlocks(other->storageBlocks),
+ combinedImageSamplers(other->combinedImageSamplers),
+ storageImages(other->storageImages)
+ {
+ }
+
+ static QShaderDescriptionPrivate *get(QShaderDescription *desc) { return desc->d; }
+ static const QShaderDescriptionPrivate *get(const QShaderDescription *desc) { return desc->d; }
+
+ QJsonDocument makeDoc();
+ void loadDoc(const QJsonDocument &doc);
+
+ QAtomicInt ref;
+ QVector<QShaderDescription::InOutVariable> inVars;
+ QVector<QShaderDescription::InOutVariable> outVars;
+ QVector<QShaderDescription::UniformBlock> uniformBlocks;
+ QVector<QShaderDescription::PushConstantBlock> pushConstantBlocks;
+ QVector<QShaderDescription::StorageBlock> storageBlocks;
+ QVector<QShaderDescription::InOutVariable> combinedImageSamplers;
+ QVector<QShaderDescription::InOutVariable> storageImages;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/rhi/rhi.pri b/src/gui/rhi/rhi.pri
new file mode 100644
index 0000000000..d8607f1024
--- /dev/null
+++ b/src/gui/rhi/rhi.pri
@@ -0,0 +1,57 @@
+HEADERS += \
+ rhi/qrhi_p.h \
+ rhi/qrhi_p_p.h \
+ rhi/qrhiprofiler_p.h \
+ rhi/qrhiprofiler_p_p.h \
+ rhi/qrhinull_p.h \
+ rhi/qrhinull_p_p.h \
+ rhi/qshader_p.h \
+ rhi/qshader_p_p.h \
+ rhi/qshaderdescription_p.h \
+ rhi/qshaderdescription_p_p.h
+
+SOURCES += \
+ rhi/qrhi.cpp \
+ rhi/qrhiprofiler.cpp \
+ rhi/qrhinull.cpp \
+ rhi/qshaderdescription.cpp \
+ rhi/qshader.cpp
+
+qtConfig(opengl) {
+ HEADERS += \
+ rhi/qrhigles2_p.h \
+ rhi/qrhigles2_p_p.h
+ SOURCES += \
+ rhi/qrhigles2.cpp
+}
+
+qtConfig(vulkan) {
+ HEADERS += \
+ rhi/qrhivulkan_p.h \
+ rhi/qrhivulkan_p_p.h
+ SOURCES += \
+ rhi/qrhivulkan.cpp
+}
+
+win32 {
+ HEADERS += \
+ rhi/qrhid3d11_p.h \
+ rhi/qrhid3d11_p_p.h
+ SOURCES += \
+ rhi/qrhid3d11.cpp
+
+ LIBS += -ld3d11 -ldxgi -ldxguid -ld3dcompiler
+}
+
+# darwin {
+macos {
+ HEADERS += \
+ rhi/qrhimetal_p.h \
+ rhi/qrhimetal_p_p.h
+ SOURCES += \
+ rhi/qrhimetal.mm
+
+ LIBS += -framework AppKit -framework Metal
+}
+
+include($$PWD/../../3rdparty/VulkanMemoryAllocator.pri)
diff --git a/src/gui/text/qabstracttextdocumentlayout.cpp b/src/gui/text/qabstracttextdocumentlayout.cpp
index 2278378613..5263ece87c 100644
--- a/src/gui/text/qabstracttextdocumentlayout.cpp
+++ b/src/gui/text/qabstracttextdocumentlayout.cpp
@@ -41,6 +41,7 @@
#include <qtextformat.h>
#include "qtextdocument_p.h"
#include "qtextengine_p.h"
+#include "qtextlist.h"
#include "qabstracttextdocumentlayout_p.h"
@@ -650,6 +651,36 @@ QTextFormat QAbstractTextDocumentLayout::formatAt(const QPointF &pos) const
}
/*!
+ \since 5.14
+
+ Returns the block (probably a list item) whose \l{QTextBlockFormat::marker()}{marker}
+ is found at the given position \a pos.
+*/
+QTextBlock QAbstractTextDocumentLayout::blockWithMarkerAt(const QPointF &pos) const
+{
+ QTextBlock block = document()->firstBlock();
+ while (block.isValid()) {
+ if (block.blockFormat().marker() != QTextBlockFormat::NoMarker) {
+ QRectF blockBr = blockBoundingRect(block);
+ QTextBlockFormat blockFmt = block.blockFormat();
+ QFontMetrics fm(block.charFormat().font());
+ qreal totalIndent = blockFmt.indent() + blockFmt.leftMargin() + blockFmt.textIndent();
+ if (block.textList())
+ totalIndent += block.textList()->format().indent() * 40;
+ QRectF adjustedBr = blockBr.adjusted(totalIndent - fm.height(), 0, totalIndent - blockBr.width(), fm.height() - blockBr.height());
+ if (adjustedBr.contains(pos)) {
+ //qDebug() << "hit block" << block.text() << blockBr << adjustedBr << "marker" << block.blockFormat().marker()
+ // << "font" << block.charFormat().font() << "adj" << lineHeight << totalIndent;
+ if (block.blockFormat().hasProperty(QTextFormat::BlockMarker))
+ return block;
+ }
+ }
+ block = block.next();
+ }
+ return QTextBlock();
+}
+
+/*!
\fn QRectF QAbstractTextDocumentLayout::frameBoundingRect(QTextFrame *frame) const
Returns the bounding rectangle of \a frame.
diff --git a/src/gui/text/qabstracttextdocumentlayout.h b/src/gui/text/qabstracttextdocumentlayout.h
index 3371401420..397dcd37d4 100644
--- a/src/gui/text/qabstracttextdocumentlayout.h
+++ b/src/gui/text/qabstracttextdocumentlayout.h
@@ -87,6 +87,7 @@ public:
QString anchorAt(const QPointF& pos) const;
QString imageAt(const QPointF &pos) const;
QTextFormat formatAt(const QPointF &pos) const;
+ QTextBlock blockWithMarkerAt(const QPointF &pos) const;
virtual int pageCount() const = 0;
virtual QSizeF documentSize() const = 0;
diff --git a/src/gui/text/qabstracttextdocumentlayout_p.h b/src/gui/text/qabstracttextdocumentlayout_p.h
index 191c463dc6..d631ce3197 100644
--- a/src/gui/text/qabstracttextdocumentlayout_p.h
+++ b/src/gui/text/qabstracttextdocumentlayout_p.h
@@ -59,7 +59,7 @@ QT_BEGIN_NAMESPACE
struct QTextObjectHandler
{
- QTextObjectHandler() : iface(0) {}
+ QTextObjectHandler() : iface(nullptr) {}
QTextObjectInterface *iface;
QPointer<QObject> component;
};
@@ -71,12 +71,12 @@ public:
Q_DECLARE_PUBLIC(QAbstractTextDocumentLayout)
inline QAbstractTextDocumentLayoutPrivate()
- : paintDevice(0) {}
+ : paintDevice(nullptr) {}
~QAbstractTextDocumentLayoutPrivate();
inline void setDocument(QTextDocument *doc) {
document = doc;
- docPrivate = 0;
+ docPrivate = nullptr;
if (doc)
docPrivate = doc->docHandle();
}
diff --git a/src/gui/text/qcssparser_p.h b/src/gui/text/qcssparser_p.h
index 860bbe382a..62578f75e5 100644
--- a/src/gui/text/qcssparser_p.h
+++ b/src/gui/text/qcssparser_p.h
@@ -467,8 +467,8 @@ struct Q_GUI_EXPORT Declaration
Attachment attachmentValue() const;
int styleFeaturesValue() const;
- bool intValue(int *i, const char *unit = 0) const;
- bool realValue(qreal *r, const char *unit = 0) const;
+ bool intValue(int *i, const char *unit = nullptr) const;
+ bool realValue(qreal *r, const char *unit = nullptr) const;
QSize sizeValue() const;
QRect rectValue() const;
@@ -584,7 +584,7 @@ struct Q_GUI_EXPORT Selector
{
QVector<BasicSelector> basicSelectors;
int specificity() const;
- quint64 pseudoClass(quint64 *negated = 0) const;
+ quint64 pseudoClass(quint64 *negated = nullptr) const;
QString pseudoElement() const;
};
QT_CSS_DECLARE_TYPEINFO(Selector, Q_MOVABLE_TYPE)
@@ -656,7 +656,7 @@ public:
};
QVector<StyleRule> styleRulesForNode(NodePtr node);
- QVector<Declaration> declarationsForNode(NodePtr node, const char *extraPseudo = 0);
+ QVector<Declaration> declarationsForNode(NodePtr node, const char *extraPseudo = nullptr);
virtual bool nodeNameEquals(NodePtr node, const QString& nodeName) const;
virtual QString attribute(NodePtr node, const QString &name) const = 0;
@@ -744,7 +744,7 @@ QT_CSS_DECLARE_TYPEINFO(Symbol, Q_MOVABLE_TYPE)
class Q_GUI_EXPORT Scanner
{
public:
- static QString preprocess(const QString &input, bool *hasEscapeSequences = 0);
+ static QString preprocess(const QString &input, bool *hasEscapeSequences = nullptr);
static void scan(const QString &preprocessedInput, QVector<Symbol> *symbols);
};
@@ -845,7 +845,7 @@ struct Q_GUI_EXPORT ValueExtractor
bool extractGeometry(int *w, int *h, int *minw, int *minh, int *maxw, int *maxh);
bool extractPosition(int *l, int *t, int *r, int *b, QCss::Origin *, Qt::Alignment *,
QCss::PositionMode *, Qt::Alignment *);
- bool extractBox(int *margins, int *paddings, int *spacing = 0);
+ 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);
diff --git a/src/gui/text/qdistancefield.cpp b/src/gui/text/qdistancefield.cpp
index 82bb617733..5967c8d3de 100644
--- a/src/gui/text/qdistancefield.cpp
+++ b/src/gui/text/qdistancefield.cpp
@@ -904,6 +904,9 @@ QDistanceField::QDistanceField(const QDistanceField &other)
d = other.d;
}
+QDistanceField &QDistanceField::operator=(const QDistanceField &)
+ = default;
+
QDistanceField::QDistanceField(const QRawFont &font, glyph_t glyph, bool doubleResolution)
{
setGlyph(font, glyph, doubleResolution);
diff --git a/src/gui/text/qdistancefield_p.h b/src/gui/text/qdistancefield_p.h
index 31cdf7edd2..dc5d5a9a02 100644
--- a/src/gui/text/qdistancefield_p.h
+++ b/src/gui/text/qdistancefield_p.h
@@ -72,7 +72,7 @@ int Q_GUI_EXPORT QT_DISTANCEFIELD_HIGHGLYPHCOUNT();
class Q_GUI_EXPORT QDistanceFieldData : public QSharedData
{
public:
- QDistanceFieldData() : glyph(0), width(0), height(0), nbytes(0), data(0) {}
+ QDistanceFieldData() : glyph(0), width(0), height(0), nbytes(0), data(nullptr) {}
QDistanceFieldData(const QDistanceFieldData &other);
~QDistanceFieldData();
@@ -95,6 +95,7 @@ public:
QDistanceField(QFontEngine *fontEngine, glyph_t glyph, bool doubleResolution = false);
QDistanceField(const QPainterPath &path, glyph_t glyph, bool doubleResolution = false);
QDistanceField(const QDistanceField &other);
+ QDistanceField &operator=(const QDistanceField &other);
bool isNull() const;
diff --git a/src/gui/text/qfont.cpp b/src/gui/text/qfont.cpp
index 1dbb03948d..0249a20cc6 100644
--- a/src/gui/text/qfont.cpp
+++ b/src/gui/text/qfont.cpp
@@ -2083,7 +2083,7 @@ QString QFont::toString() const
\relates QFont
\since 5.3
*/
-uint qHash(const QFont &font, uint seed) Q_DECL_NOTHROW
+uint qHash(const QFont &font, uint seed) noexcept
{
return qHash(QFontPrivate::get(font)->request, seed);
}
@@ -3164,7 +3164,104 @@ void QFontCache::decreaseCache()
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug stream, const QFont &font)
{
- return stream << "QFont(" << font.toString() << ')';
+ QDebugStateSaver saver(stream);
+ stream.nospace().noquote();
+ stream << "QFont(";
+
+ if (stream.verbosity() == QDebug::DefaultVerbosity) {
+ stream << font.toString() << ")";
+ return stream;
+ }
+
+ QString fontDescription;
+ QDebug debug(&fontDescription);
+ debug.nospace();
+
+ QFontPrivate priv;
+ const QFont defaultFont(&priv);
+
+ for (int property = QFont::FamilyResolved; property < QFont::AllPropertiesResolved; property <<= 1) {
+ const bool resolved = (font.resolve_mask & property) != 0;
+ if (!resolved && stream.verbosity() == QDebug::MinimumVerbosity)
+ continue;
+
+ #define QFONT_DEBUG_SKIP_DEFAULT(prop) \
+ if ((font.prop() == defaultFont.prop()) && stream.verbosity() == 1) \
+ continue;
+
+ QDebugStateSaver saver(debug);
+
+ switch (property) {
+ case QFont::FamilyResolved:
+ debug << font.family(); break;
+ case QFont::SizeResolved:
+ if (font.pointSizeF() >= 0)
+ debug << font.pointSizeF() << "pt";
+ else if (font.pixelSize() >= 0)
+ debug << font.pixelSize() << "px";
+ else
+ Q_UNREACHABLE();
+ break;
+ case QFont::StyleHintResolved:
+ QFONT_DEBUG_SKIP_DEFAULT(styleHint);
+ debug.verbosity(1) << font.styleHint(); break;
+ case QFont::StyleStrategyResolved:
+ QFONT_DEBUG_SKIP_DEFAULT(styleStrategy);
+ debug.verbosity(1) << font.styleStrategy(); break;
+ case QFont::WeightResolved:
+ debug.verbosity(1) << QFont::Weight(font.weight()); break;
+ case QFont::StyleResolved:
+ QFONT_DEBUG_SKIP_DEFAULT(style);
+ debug.verbosity(0) << font.style(); break;
+ case QFont::UnderlineResolved:
+ QFONT_DEBUG_SKIP_DEFAULT(underline);
+ debug << "underline=" << font.underline(); break;
+ case QFont::OverlineResolved:
+ QFONT_DEBUG_SKIP_DEFAULT(overline);
+ debug << "overline=" << font.overline(); break;
+ case QFont::StrikeOutResolved:
+ QFONT_DEBUG_SKIP_DEFAULT(strikeOut);
+ debug << "strikeOut=" << font.strikeOut(); break;
+ case QFont::FixedPitchResolved:
+ QFONT_DEBUG_SKIP_DEFAULT(fixedPitch);
+ debug << "fixedPitch=" << font.fixedPitch(); break;
+ case QFont::StretchResolved:
+ QFONT_DEBUG_SKIP_DEFAULT(stretch);
+ debug.verbosity(0) << QFont::Stretch(font.stretch()); break;
+ case QFont::KerningResolved:
+ QFONT_DEBUG_SKIP_DEFAULT(kerning);
+ debug << "kerning=" << font.kerning(); break;
+ case QFont::CapitalizationResolved:
+ QFONT_DEBUG_SKIP_DEFAULT(capitalization);
+ debug.verbosity(0) << font.capitalization(); break;
+ case QFont::LetterSpacingResolved:
+ QFONT_DEBUG_SKIP_DEFAULT(letterSpacing);
+ debug << "letterSpacing=" << font.letterSpacing();
+ debug.verbosity(0) << " (" << font.letterSpacingType() << ")";
+ break;
+ case QFont::HintingPreferenceResolved:
+ QFONT_DEBUG_SKIP_DEFAULT(hintingPreference);
+ debug.verbosity(0) << font.hintingPreference(); break;
+ case QFont::StyleNameResolved:
+ QFONT_DEBUG_SKIP_DEFAULT(styleName);
+ debug << "styleName=" << font.styleName(); break;
+ default:
+ continue;
+ };
+
+ #undef QFONT_DEBUG_SKIP_DEFAULT
+
+ debug << ", ";
+ }
+
+ if (stream.verbosity() > QDebug::MinimumVerbosity)
+ debug.verbosity(0) << "resolveMask=" << QFlags<QFont::ResolveProperties>(font.resolve_mask);
+ else
+ fontDescription.chop(2); // Last ', '
+
+ stream << fontDescription << ')';
+
+ return stream;
}
#endif
diff --git a/src/gui/text/qfont.h b/src/gui/text/qfont.h
index e86f06353a..683aa3bf65 100644
--- a/src/gui/text/qfont.h
+++ b/src/gui/text/qfont.h
@@ -147,6 +147,7 @@ public:
Q_ENUM(SpacingType)
enum ResolveProperties {
+ NoPropertiesResolved = 0x0000,
FamilyResolved = 0x0001,
SizeResolved = 0x0002,
StyleHintResolved = 0x0004,
@@ -167,6 +168,7 @@ public:
FamiliesResolved = 0x20000,
AllPropertiesResolved = 0x3ffff
};
+ Q_ENUM(ResolveProperties)
QFont();
QFont(const QString &family, int pointSize = -1, int weight = -1, bool italic = false);
@@ -259,10 +261,8 @@ public:
bool operator<(const QFont &) const;
operator QVariant() const;
bool isCopyOf(const QFont &) const;
-#ifdef Q_COMPILER_RVALUE_REFS
- inline QFont &operator=(QFont &&other) Q_DECL_NOEXCEPT
+ inline QFont &operator=(QFont &&other) noexcept
{ qSwap(d, other.d); qSwap(resolve_mask, other.resolve_mask); return *this; }
-#endif
#if QT_DEPRECATED_SINCE(5, 3)
// needed for X11
@@ -335,13 +335,17 @@ private:
friend Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QFont &);
#endif
+#ifndef QT_NO_DEBUG_STREAM
+ friend Q_GUI_EXPORT QDebug operator<<(QDebug, const QFont &);
+#endif
+
QExplicitlySharedDataPointer<QFontPrivate> d;
uint resolve_mask;
};
Q_DECLARE_SHARED(QFont)
-Q_GUI_EXPORT uint qHash(const QFont &font, uint seed = 0) Q_DECL_NOTHROW;
+Q_GUI_EXPORT uint qHash(const QFont &font, uint seed = 0) noexcept;
inline bool QFont::bold() const
{ return weight() > Medium; }
diff --git a/src/gui/text/qfont_p.h b/src/gui/text/qfont_p.h
index e86ec31e47..466e19e9cc 100644
--- a/src/gui/text/qfont_p.h
+++ b/src/gui/text/qfont_p.h
@@ -136,21 +136,22 @@ struct QFontDef
}
};
-inline uint qHash(const QFontDef &fd, uint seed = 0) Q_DECL_NOTHROW
+inline uint qHash(const QFontDef &fd, uint seed = 0) noexcept
{
- return qHash(qRound64(fd.pixelSize*10000)) // use only 4 fractional digits
- ^ qHash(fd.weight)
- ^ qHash(fd.style)
- ^ qHash(fd.stretch)
- ^ qHash(fd.styleHint)
- ^ qHash(fd.styleStrategy)
- ^ qHash(fd.ignorePitch)
- ^ qHash(fd.fixedPitch)
- ^ qHash(fd.family, seed)
- ^ qHash(fd.families, seed)
- ^ qHash(fd.styleName)
- ^ qHash(fd.hintingPreference)
- ;
+ QtPrivate::QHashCombine hash;
+ seed = hash(seed, qRound64(fd.pixelSize*10000)); // use only 4 fractional digits
+ seed = hash(seed, fd.weight);
+ seed = hash(seed, fd.style);
+ seed = hash(seed, fd.stretch);
+ seed = hash(seed, fd.styleHint);
+ seed = hash(seed, fd.styleStrategy);
+ seed = hash(seed, fd.ignorePitch);
+ seed = hash(seed, fd.fixedPitch);
+ seed = hash(seed, fd.family);
+ seed = hash(seed, fd.families);
+ seed = hash(seed, fd.styleName);
+ seed = hash(seed, fd.hintingPreference);
+ return seed;
}
class QFontEngineData
@@ -266,7 +267,7 @@ public:
// QFontEngine cache
struct Engine {
- Engine() : data(0), timestamp(0), hits(0) { }
+ Engine() : data(nullptr), timestamp(0), hits(0) { }
Engine(QFontEngine *d) : data(d), timestamp(0), hits(0) { }
QFontEngine *data;
diff --git a/src/gui/text/qfontdatabase.cpp b/src/gui/text/qfontdatabase.cpp
index bc7d379157..36104991c3 100644
--- a/src/gui/text/qfontdatabase.cpp
+++ b/src/gui/text/qfontdatabase.cpp
@@ -422,7 +422,7 @@ struct FallbacksCacheKey {
QChar::Script script;
};
-inline bool operator==(const FallbacksCacheKey &lhs, const FallbacksCacheKey &rhs) Q_DECL_NOTHROW
+inline bool operator==(const FallbacksCacheKey &lhs, const FallbacksCacheKey &rhs) noexcept
{
return lhs.script == rhs.script &&
lhs.styleHint == rhs.styleHint &&
@@ -430,12 +430,12 @@ inline bool operator==(const FallbacksCacheKey &lhs, const FallbacksCacheKey &rh
lhs.family == rhs.family;
}
-inline bool operator!=(const FallbacksCacheKey &lhs, const FallbacksCacheKey &rhs) Q_DECL_NOTHROW
+inline bool operator!=(const FallbacksCacheKey &lhs, const FallbacksCacheKey &rhs) noexcept
{
return !operator==(lhs, rhs);
}
-inline uint qHash(const FallbacksCacheKey &key, uint seed = 0) Q_DECL_NOTHROW
+inline uint qHash(const FallbacksCacheKey &key, uint seed = 0) noexcept
{
QtPrivate::QHashCombine hash;
seed = hash(seed, key.family);
@@ -793,6 +793,13 @@ QString qt_resolveFontFamilyAlias(const QString &alias)
return alias;
}
+bool qt_isFontFamilyPopulated(const QString &familyName)
+{
+ QFontDatabasePrivate *d = privateDb();
+ QtFontFamily *f = d->family(familyName, QFontDatabasePrivate::RequestFamily);
+ return f != nullptr && f->populated;
+}
+
/*!
Returns a list of alternative fonts for the specified \a family and
\a style and \a script using the \a styleHint given.
diff --git a/src/gui/text/qfontengine.cpp b/src/gui/text/qfontengine.cpp
index 1719855e68..537d4bcefd 100644
--- a/src/gui/text/qfontengine.cpp
+++ b/src/gui/text/qfontengine.cpp
@@ -1059,15 +1059,15 @@ void QFontEngine::setGlyphCache(const void *context, QFontEngineGlyphCache *cach
Q_ASSERT(cache);
GlyphCaches &caches = m_glyphCaches[context];
- for (GlyphCaches::const_iterator it = caches.constBegin(), end = caches.constEnd(); it != end; ++it) {
- if (cache == it->cache.data())
+ for (auto & e : caches) {
+ if (cache == e.cache.data())
return;
}
// Limit the glyph caches to 4 per context. This covers all 90 degree rotations,
// and limits memory use when there is continuous or random rotation
if (caches.size() == 4)
- caches.removeLast();
+ caches.pop_back();
GlyphCacheEntry entry;
entry.cache = cache;
@@ -1081,8 +1081,8 @@ QFontEngineGlyphCache *QFontEngine::glyphCache(const void *context, GlyphFormat
if (caches == m_glyphCaches.cend())
return nullptr;
- for (GlyphCaches::const_iterator it = caches->begin(), end = caches->end(); it != end; ++it) {
- QFontEngineGlyphCache *cache = it->cache.data();
+ for (auto &e : *caches) {
+ QFontEngineGlyphCache *cache = e.cache.data();
if (format == cache->glyphFormat() && qtransform_equals_no_translate(cache->m_transform, transform))
return cache;
}
@@ -1213,7 +1213,7 @@ void QFontEngine::loadKerningPairs(QFixed scalingFactor)
end:
std::sort(kerning_pairs.begin(), kerning_pairs.end());
// for (int i = 0; i < kerning_pairs.count(); ++i)
-// qDebug() << 'i' << i << "left_right" << hex << kerning_pairs.at(i).left_right;
+// qDebug() << 'i' << i << "left_right" << Qt::hex << kerning_pairs.at(i).left_right;
}
diff --git a/src/gui/text/qfontengine_p.h b/src/gui/text/qfontengine_p.h
index 708c79c2ae..8dcfd7d66c 100644
--- a/src/gui/text/qfontengine_p.h
+++ b/src/gui/text/qfontengine_p.h
@@ -54,7 +54,6 @@
#include <QtGui/private/qtguiglobal_p.h>
#include "QtCore/qatomic.h"
#include <QtCore/qvarlengtharray.h>
-#include <QtCore/QLinkedList>
#include <QtCore/qhashfunctions.h>
#include "private/qtextengine_p.h"
#include "private/qfont_p.h"
@@ -194,7 +193,7 @@ public:
virtual QImage *lockedAlphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition,
GlyphFormat neededFormat,
const QTransform &t = QTransform(),
- QPoint *offset = 0);
+ QPoint *offset = nullptr);
virtual void unlockAlphaMapForGlyph();
virtual bool hasInternalCaching() const { return false; }
@@ -224,7 +223,7 @@ public:
virtual qreal minLeftBearing() const;
virtual qreal minRightBearing() const;
- virtual void getGlyphBearings(glyph_t glyph, qreal *leftBearing = 0, qreal *rightBearing = 0);
+ virtual void getGlyphBearings(glyph_t glyph, qreal *leftBearing = nullptr, qreal *rightBearing = nullptr);
inline bool canRender(uint ucs4) const { return glyphIndex(ucs4) != 0; }
virtual bool canRender(const QChar *str, int len) const;
@@ -234,7 +233,7 @@ public:
virtual int glyphCount() const;
virtual int glyphMargin(GlyphFormat format) { return format == Format_A32 ? 2 : 0; }
- virtual QFontEngine *cloneWithSize(qreal /*pixelSize*/) const { return 0; }
+ virtual QFontEngine *cloneWithSize(qreal /*pixelSize*/) const { return nullptr; }
virtual Qt::HANDLE handle() const;
@@ -292,33 +291,33 @@ public:
Holder() : ptr(nullptr), destroy_func(nullptr) {}
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) Q_DECL_NOTHROW
+ Holder(Holder &&other) noexcept
: ptr(other.ptr),
destroy_func(other.destroy_func)
{
other.ptr = nullptr;
other.destroy_func = nullptr;
}
- Holder &operator=(Holder &&other) Q_DECL_NOTHROW
+ Holder &operator=(Holder &&other) noexcept
{ swap(other); return *this; }
- void swap(Holder &other) Q_DECL_NOTHROW
+ void swap(Holder &other) noexcept
{
qSwap(ptr, other.ptr);
qSwap(destroy_func, other.destroy_func);
}
- void *get() const Q_DECL_NOTHROW { return ptr; }
- void *release() Q_DECL_NOTHROW {
+ void *get() const noexcept { return ptr; }
+ void *release() noexcept {
void *result = ptr;
ptr = nullptr;
destroy_func = nullptr;
return result;
}
- void reset() Q_DECL_NOTHROW { Holder().swap(*this); }
- qt_destroy_func_t get_deleter() const Q_DECL_NOTHROW { return destroy_func; }
+ void reset() noexcept { Holder().swap(*this); }
+ qt_destroy_func_t get_deleter() const noexcept { return destroy_func; }
- bool operator!() const Q_DECL_NOTHROW { return !ptr; }
+ bool operator!() const noexcept { return !ptr; }
};
mutable Holder font_; // \ NOTE: Declared before m_glyphCaches, so font_, face_
@@ -370,7 +369,7 @@ private:
QExplicitlySharedDataPointer<QFontEngineGlyphCache> cache;
bool operator==(const GlyphCacheEntry &other) const { return cache == other.cache; }
};
- typedef QLinkedList<GlyphCacheEntry> GlyphCaches;
+ typedef std::list<GlyphCacheEntry> GlyphCaches;
mutable QHash<const void *, GlyphCaches> m_glyphCaches;
private:
@@ -390,7 +389,7 @@ inline bool operator ==(const QFontEngine::FaceId &f1, const QFontEngine::FaceId
}
inline uint qHash(const QFontEngine::FaceId &f, uint seed = 0)
- Q_DECL_NOEXCEPT_EXPR(noexcept(qHash(f.filename)))
+ noexcept(noexcept(qHash(f.filename)))
{
QtPrivate::QHashCombine hash;
seed = hash(seed, f.filename);
@@ -458,7 +457,7 @@ public:
virtual void recalcAdvances(QGlyphLayout *, ShaperFlags) const override;
virtual void doKerning(QGlyphLayout *, ShaperFlags) const override;
virtual void addOutlineToPath(qreal, qreal, const QGlyphLayout &, QPainterPath *, QTextItem::RenderFlags flags) override;
- virtual void getGlyphBearings(glyph_t glyph, qreal *leftBearing = 0, qreal *rightBearing = 0) override;
+ virtual void getGlyphBearings(glyph_t glyph, qreal *leftBearing = nullptr, qreal *rightBearing = nullptr) override;
virtual QFixed ascent() const override;
virtual QFixed capHeight() const override;
diff --git a/src/gui/text/qfontengine_qpf2.cpp b/src/gui/text/qfontengine_qpf2.cpp
index 110d512d39..409176d41b 100644
--- a/src/gui/text/qfontengine_qpf2.cpp
+++ b/src/gui/text/qfontengine_qpf2.cpp
@@ -140,9 +140,9 @@ static inline const uchar *verifyTag(const uchar *tagPtr, const uchar *endPtr)
}
#if defined(DEBUG_HEADER)
if (length == 1)
- qDebug() << "tag data" << hex << *tagPtr;
+ qDebug() << "tag data" << Qt::hex << *tagPtr;
else if (length == 4)
- qDebug() << "tag data" << hex << tagPtr[0] << tagPtr[1] << tagPtr[2] << tagPtr[3];
+ qDebug() << "tag data" << Qt::hex << tagPtr[0] << tagPtr[1] << tagPtr[2] << tagPtr[3];
#endif
}
return tagPtr + length;
@@ -367,7 +367,7 @@ bool QFontEngineQPF2::stringToCMap(const QChar *str, int len, QGlyphLayout *glyp
#if 0 && defined(DEBUG_FONTENGINE)
QChar c(uc);
if (!findGlyph(glyphs[glyph_pos].glyph) && !seenGlyphs.contains(c))
- qDebug() << "glyph for character" << c << '/' << hex << uc << "is" << dec << glyphs[glyph_pos].glyph;
+ qDebug() << "glyph for character" << c << '/' << Qt::hex << uc << "is" << Qt::dec << glyphs[glyph_pos].glyph;
seenGlyphs.insert(c);
#endif
diff --git a/src/gui/text/qfontmetrics.h b/src/gui/text/qfontmetrics.h
index 61931fa9bc..02ff335e68 100644
--- a/src/gui/text/qfontmetrics.h
+++ b/src/gui/text/qfontmetrics.h
@@ -76,12 +76,10 @@ public:
~QFontMetrics();
QFontMetrics &operator=(const QFontMetrics &);
-#ifdef Q_COMPILER_RVALUE_REFS
- inline QFontMetrics &operator=(QFontMetrics &&other) Q_DECL_NOEXCEPT
+ inline QFontMetrics &operator=(QFontMetrics &&other) noexcept
{ qSwap(d, other.d); return *this; }
-#endif
- void swap(QFontMetrics &other) Q_DECL_NOEXCEPT
+ void swap(QFontMetrics &other) noexcept
{ qSwap(d, other.d); }
int ascent() const;
@@ -172,12 +170,10 @@ public:
QFontMetricsF &operator=(const QFontMetricsF &);
QFontMetricsF &operator=(const QFontMetrics &);
-#ifdef Q_COMPILER_RVALUE_REFS
- inline QFontMetricsF &operator=(QFontMetricsF &&other)
+ inline QFontMetricsF &operator=(QFontMetricsF &&other) noexcept
{ qSwap(d, other.d); return *this; }
-#endif
- void swap(QFontMetricsF &other) { qSwap(d, other.d); }
+ void swap(QFontMetricsF &other) noexcept { qSwap(d, other.d); }
qreal ascent() const;
qreal capHeight() const;
diff --git a/src/gui/text/qfragmentmap_p.h b/src/gui/text/qfragmentmap_p.h
index 35f60ac961..1d781352f8 100644
--- a/src/gui/text/qfragmentmap_p.h
+++ b/src/gui/text/qfragmentmap_p.h
@@ -216,7 +216,7 @@ private:
template <class Fragment>
QFragmentMapData<Fragment>::QFragmentMapData()
- : fragments(0)
+ : fragments(nullptr)
{
init();
}
diff --git a/src/gui/text/qglyphrun.h b/src/gui/text/qglyphrun.h
index 6182c4f749..15e315bea2 100644
--- a/src/gui/text/qglyphrun.h
+++ b/src/gui/text/qglyphrun.h
@@ -66,13 +66,11 @@ public:
QGlyphRun();
QGlyphRun(const QGlyphRun &other);
-#ifdef Q_COMPILER_RVALUE_REFS
- QGlyphRun &operator=(QGlyphRun &&other) Q_DECL_NOTHROW { swap(other); return *this; }
-#endif
+ QGlyphRun &operator=(QGlyphRun &&other) noexcept { swap(other); return *this; }
QGlyphRun &operator=(const QGlyphRun &other);
~QGlyphRun();
- void swap(QGlyphRun &other) Q_DECL_NOTHROW { qSwap(d, other.d); }
+ void swap(QGlyphRun &other) noexcept { qSwap(d, other.d); }
QRawFont rawFont() const;
void setRawFont(const QRawFont &rawFont);
diff --git a/src/gui/text/qglyphrun_p.h b/src/gui/text/qglyphrun_p.h
index 5b6bdad648..465c3c7000 100644
--- a/src/gui/text/qglyphrun_p.h
+++ b/src/gui/text/qglyphrun_p.h
@@ -65,7 +65,7 @@ class QGlyphRunPrivate: public QSharedData
{
public:
QGlyphRunPrivate()
- : flags(0)
+ : flags(nullptr)
, glyphIndexData(glyphIndexes.constData())
, glyphIndexDataSize(0)
, glyphPositionData(glyphPositions.constData())
diff --git a/src/gui/text/qplatformfontdatabase.cpp b/src/gui/text/qplatformfontdatabase.cpp
index a911014a19..715b00d838 100644
--- a/src/gui/text/qplatformfontdatabase.cpp
+++ b/src/gui/text/qplatformfontdatabase.cpp
@@ -60,6 +60,7 @@ void qt_registerFont(const QString &familyname, const QString &stylename,
void qt_registerFontFamily(const QString &familyName);
void qt_registerAliasToFontFamily(const QString &familyName, const QString &alias);
+bool qt_isFontFamilyPopulated(const QString &familyName);
/*!
Registers the pre-rendered QPF2 font contained in the given \a dataArray.
@@ -666,6 +667,16 @@ void QPlatformFontDatabase::registerAliasToFontFamily(const QString &familyName,
}
/*!
+ Helper function that returns true if the font family has already been registered and populated.
+
+ \since 5.14
+*/
+bool QPlatformFontDatabase::isFamilyPopulated(const QString &familyName)
+{
+ return qt_isFontFamilyPopulated(familyName);
+}
+
+/*!
\class QPlatformFontDatabase
\since 5.0
\internal
diff --git a/src/gui/text/qplatformfontdatabase.h b/src/gui/text/qplatformfontdatabase.h
index f4558129a7..38ba7f10b2 100644
--- a/src/gui/text/qplatformfontdatabase.h
+++ b/src/gui/text/qplatformfontdatabase.h
@@ -139,6 +139,8 @@ public:
static void registerFontFamily(const QString &familyName);
static void registerAliasToFontFamily(const QString &familyName, const QString &alias);
+
+ static bool isFamilyPopulated(const QString &familyName);
};
QT_END_NAMESPACE
diff --git a/src/gui/text/qrawfont.cpp b/src/gui/text/qrawfont.cpp
index b2d8bf01af..a060448924 100644
--- a/src/gui/text/qrawfont.cpp
+++ b/src/gui/text/qrawfont.cpp
@@ -322,7 +322,7 @@ bool QRawFont::operator==(const QRawFont &other) const
\relates QRawFont
\since 5.8
*/
-uint qHash(const QRawFont &font, uint seed) Q_DECL_NOTHROW
+uint qHash(const QRawFont &font, uint seed) noexcept
{
return qHash(QRawFontPrivate::get(font)->fontEngine, seed);
}
diff --git a/src/gui/text/qrawfont.h b/src/gui/text/qrawfont.h
index 470f2694e4..c6289d6c93 100644
--- a/src/gui/text/qrawfont.h
+++ b/src/gui/text/qrawfont.h
@@ -79,13 +79,11 @@ public:
qreal pixelSize,
QFont::HintingPreference hintingPreference = QFont::PreferDefaultHinting);
QRawFont(const QRawFont &other);
-#ifdef Q_COMPILER_RVALUE_REFS
- QRawFont &operator=(QRawFont &&other) Q_DECL_NOTHROW { swap(other); return *this; }
-#endif
+ QRawFont &operator=(QRawFont &&other) noexcept { swap(other); return *this; }
QRawFont &operator=(const QRawFont &other);
~QRawFont();
- void swap(QRawFont &other) Q_DECL_NOTHROW { qSwap(d, other.d); }
+ void swap(QRawFont &other) noexcept { qSwap(d, other.d); }
bool isValid() const;
@@ -158,7 +156,7 @@ Q_DECLARE_SHARED(QRawFont)
Q_DECLARE_OPERATORS_FOR_FLAGS(QRawFont::LayoutFlags)
-Q_GUI_EXPORT uint qHash(const QRawFont &font, uint seed = 0) Q_DECL_NOTHROW;
+Q_GUI_EXPORT uint qHash(const QRawFont &font, uint seed = 0) noexcept;
inline QVector<QPointF> QRawFont::advancesForGlyphIndexes(const QVector<quint32> &glyphIndexes, QRawFont::LayoutFlags layoutFlags) const
{
diff --git a/src/gui/text/qrawfont_p.h b/src/gui/text/qrawfont_p.h
index 0fc8739bfb..dced165475 100644
--- a/src/gui/text/qrawfont_p.h
+++ b/src/gui/text/qrawfont_p.h
@@ -67,9 +67,9 @@ class Q_GUI_EXPORT QRawFontPrivate
{
public:
QRawFontPrivate()
- : fontEngine(0)
+ : fontEngine(nullptr)
, hintingPreference(QFont::PreferDefaultHinting)
- , thread(0)
+ , thread(nullptr)
{}
QRawFontPrivate(const QRawFontPrivate &other)
@@ -78,9 +78,9 @@ public:
, thread(other.thread)
{
#ifndef QT_NO_DEBUG
- Q_ASSERT(fontEngine == 0 || thread == QThread::currentThread());
+ Q_ASSERT(fontEngine == nullptr || thread == QThread::currentThread());
#endif
- if (fontEngine != 0)
+ if (fontEngine != nullptr)
fontEngine->ref.ref();
}
@@ -94,37 +94,37 @@ public:
inline void cleanUp()
{
- setFontEngine(0);
+ setFontEngine(nullptr);
hintingPreference = QFont::PreferDefaultHinting;
}
inline bool isValid() const
{
#ifndef QT_NO_DEBUG
- Q_ASSERT(fontEngine == 0 || thread == QThread::currentThread());
+ Q_ASSERT(fontEngine == nullptr || thread == QThread::currentThread());
#endif
- return fontEngine != 0;
+ return fontEngine != nullptr;
}
inline void setFontEngine(QFontEngine *engine)
{
#ifndef QT_NO_DEBUG
- Q_ASSERT(fontEngine == 0 || thread == QThread::currentThread());
+ Q_ASSERT(fontEngine == nullptr || thread == QThread::currentThread());
#endif
if (fontEngine == engine)
return;
- if (fontEngine != 0) {
+ if (fontEngine != nullptr) {
if (!fontEngine->ref.deref())
delete fontEngine;
#ifndef QT_NO_DEBUG
- thread = 0;
+ thread = nullptr;
#endif
}
fontEngine = engine;
- if (fontEngine != 0) {
+ if (fontEngine != nullptr) {
fontEngine->ref.ref();
#ifndef QT_NO_DEBUG
thread = QThread::currentThread();
diff --git a/src/gui/text/qstatictext.h b/src/gui/text/qstatictext.h
index ada0456b8f..e8c94a6add 100644
--- a/src/gui/text/qstatictext.h
+++ b/src/gui/text/qstatictext.h
@@ -64,13 +64,11 @@ public:
QStaticText();
explicit QStaticText(const QString &text);
QStaticText(const QStaticText &other);
-#ifdef Q_COMPILER_RVALUE_REFS
- QStaticText &operator=(QStaticText &&other) Q_DECL_NOTHROW { swap(other); return *this; }
-#endif
+ QStaticText &operator=(QStaticText &&other) noexcept { swap(other); return *this; }
QStaticText &operator=(const QStaticText &);
~QStaticText();
- void swap(QStaticText &other) Q_DECL_NOTHROW { qSwap(data, other.data); }
+ void swap(QStaticText &other) noexcept { qSwap(data, other.data); }
void setText(const QString &text);
QString text() const;
diff --git a/src/gui/text/qstatictext_p.h b/src/gui/text/qstatictext_p.h
index 4ec09297c5..8d6792216d 100644
--- a/src/gui/text/qstatictext_p.h
+++ b/src/gui/text/qstatictext_p.h
@@ -80,7 +80,7 @@ class Q_GUI_EXPORT QStaticTextItem
public:
QStaticTextItem() : useBackendOptimizations(false),
userDataNeedsUpdate(0), usesRawFont(0),
- m_fontEngine(0), m_userData(0) {}
+ m_fontEngine(nullptr), m_userData(nullptr) {}
void setUserData(QStaticTextUserData *newUserData)
{
diff --git a/src/gui/text/qsyntaxhighlighter.cpp b/src/gui/text/qsyntaxhighlighter.cpp
index 102a776ed3..cf584f6980 100644
--- a/src/gui/text/qsyntaxhighlighter.cpp
+++ b/src/gui/text/qsyntaxhighlighter.cpp
@@ -45,6 +45,7 @@
#include <private/qtextdocument_p.h>
#include <qtextlayout.h>
#include <qpointer.h>
+#include <qscopedvaluerollback.h>
#include <qtextobject.h>
#include <qtextcursor.h>
#include <qdebug.h>
@@ -68,14 +69,14 @@ public:
void reformatBlocks(int from, int charsRemoved, int charsAdded);
void reformatBlock(const QTextBlock &block);
- inline void rehighlight(QTextCursor &cursor, QTextCursor::MoveOperation operation) {
- inReformatBlocks = true;
+ inline void rehighlight(QTextCursor &cursor, QTextCursor::MoveOperation operation)
+ {
+ QScopedValueRollback<bool> bg(inReformatBlocks, true);
cursor.beginEditBlock();
int from = cursor.position();
cursor.movePosition(operation);
reformatBlocks(from, 0, cursor.position() - from);
cursor.endEditBlock();
- inReformatBlocks = false;
}
inline void _q_delayedRehighlight() {
diff --git a/src/gui/text/qtextcursor.h b/src/gui/text/qtextcursor.h
index 1a00b753ad..7cad3cc5e8 100644
--- a/src/gui/text/qtextcursor.h
+++ b/src/gui/text/qtextcursor.h
@@ -73,13 +73,11 @@ public:
explicit QTextCursor(QTextFrame *frame);
explicit QTextCursor(const QTextBlock &block);
QTextCursor(const QTextCursor &cursor);
-#ifdef Q_COMPILER_RVALUE_REFS
- QTextCursor &operator=(QTextCursor &&other) Q_DECL_NOTHROW { swap(other); return *this; }
-#endif
+ QTextCursor &operator=(QTextCursor &&other) noexcept { swap(other); return *this; }
QTextCursor &operator=(const QTextCursor &other);
~QTextCursor();
- void swap(QTextCursor &other) Q_DECL_NOTHROW { qSwap(d, other.d); }
+ void swap(QTextCursor &other) noexcept { qSwap(d, other.d); }
bool isNull() const;
diff --git a/src/gui/text/qtextdocument.cpp b/src/gui/text/qtextdocument.cpp
index 4e5ba8e038..757e2086e5 100644
--- a/src/gui/text/qtextdocument.cpp
+++ b/src/gui/text/qtextdocument.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtGui module of the Qt Toolkit.
@@ -70,6 +70,12 @@
#include <private/qabstracttextdocumentlayout_p.h>
#include "qpagedpaintdevice.h"
#include "private/qpagedpaintdevice_p.h"
+#if QT_CONFIG(textmarkdownreader)
+#include <private/qtextmarkdownimporter_p.h>
+#endif
+#if QT_CONFIG(textmarkdownwriter)
+#include <private/qtextmarkdownwriter_p.h>
+#endif
#include <limits.h>
@@ -2072,6 +2078,7 @@ void QTextDocument::print(QPagedPaintDevice *printer) const
The icon needs to be converted to one of the supported types first,
for example using QIcon::pixmap.
\value StyleSheetResource The resource contains CSS.
+ \value MarkdownResource The resource contains Markdown.
\value UserResource The first available value for user defined
resource types.
@@ -2704,6 +2711,12 @@ void QTextHtmlExporter::emitFragment(const QTextFragment &fragment)
if (imgFmt.hasProperty(QTextFormat::ImageName))
emitAttribute("src", imgFmt.name());
+ if (imgFmt.hasProperty(QTextFormat::ImageAltText))
+ emitAttribute("alt", imgFmt.stringProperty(QTextFormat::ImageAltText));
+
+ if (imgFmt.hasProperty(QTextFormat::ImageTitle))
+ emitAttribute("title", imgFmt.stringProperty(QTextFormat::ImageTitle));
+
if (imgFmt.hasProperty(QTextFormat::ImageWidth))
emitAttribute("width", QString::number(imgFmt.width()));
@@ -3288,6 +3301,62 @@ QString QTextDocument::toHtml(const QByteArray &encoding) const
#endif // QT_NO_TEXTHTMLPARSER
/*!
+ \since 5.14
+ Returns a string containing a Markdown representation of the document with
+ the given \a features, or an empty string if writing fails for any reason.
+
+ \sa setMarkdown
+*/
+#if QT_CONFIG(textmarkdownwriter)
+QString QTextDocument::toMarkdown(QTextDocument::MarkdownFeatures features) const
+{
+ QString ret;
+ QTextStream s(&ret);
+ QTextMarkdownWriter w(s, features);
+ if (w.writeAll(this))
+ return ret;
+ return QString();
+}
+#endif
+
+/*!
+ \since 5.14
+ Replaces the entire contents of the document with the given
+ Markdown-formatted text in the \a markdown string, with the given
+ \a features supported. By default, all supported GitHub-style
+ Markdown features are included; pass \c MarkdownDialectCommonMark
+ for a more basic parse.
+
+ The Markdown formatting is respected as much as possible; for example,
+ "*bold* text" will produce text where the first word has a font weight that
+ gives it an emphasized appearance.
+
+ Parsing of HTML included in the \a markdown string is handled in the same
+ way as in \l setHtml; however, Markdown formatting inside HTML blocks is
+ not supported.
+
+ Some features of the parser can be enabled or disabled via the \a features
+ argument:
+
+ \value MarkdownNoHTML
+ Any HTML tags in the Markdown text will be discarded
+ \value MarkdownDialectCommonMark
+ The parser supports only the features standardized by CommonMark
+ \value MarkdownDialectGitHub
+ The parser supports the GitHub dialect
+
+ The default is \c MarkdownDialectGitHub.
+
+ The undo/redo history is reset when this function is called.
+*/
+#if QT_CONFIG(textmarkdownreader)
+void QTextDocument::setMarkdown(const QString &markdown, QTextDocument::MarkdownFeatures features)
+{
+ QTextMarkdownImporter(static_cast<QTextMarkdownImporter::Features>(int(features))).import(this, markdown);
+}
+#endif
+
+/*!
Returns a vector of text formats for all the formats used in the document.
*/
QVector<QTextFormat> QTextDocument::allFormats() const
diff --git a/src/gui/text/qtextdocument.h b/src/gui/text/qtextdocument.h
index c9b22e053b..edb6bd9310 100644
--- a/src/gui/text/qtextdocument.h
+++ b/src/gui/text/qtextdocument.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtGui module of the Qt Toolkit.
@@ -151,6 +151,25 @@ public:
void setHtml(const QString &html);
#endif
+#if QT_CONFIG(textmarkdownwriter) || QT_CONFIG(textmarkdownreader)
+ // Must be in sync with QTextMarkdownImporter::Features, should be in sync with #define MD_FLAG_* in md4c
+ enum MarkdownFeature {
+ MarkdownNoHTML = 0x0020 | 0x0040,
+ MarkdownDialectCommonMark = 0,
+ MarkdownDialectGitHub = 0x0004 | 0x0008 | 0x0400 | 0x0100 | 0x0200 | 0x0800
+ };
+ Q_DECLARE_FLAGS(MarkdownFeatures, MarkdownFeature)
+ Q_FLAG(MarkdownFeatures)
+#endif
+
+#if QT_CONFIG(textmarkdownwriter)
+ QString toMarkdown(MarkdownFeatures features = MarkdownDialectGitHub) const;
+#endif
+
+#if QT_CONFIG(textmarkdownreader)
+ void setMarkdown(const QString &markdown, MarkdownFeatures features = MarkdownDialectGitHub);
+#endif
+
QString toRawText() const;
QString toPlainText() const;
void setPlainText(const QString &text);
@@ -209,6 +228,7 @@ public:
HtmlResource = 1,
ImageResource = 2,
StyleSheetResource = 3,
+ MarkdownResource = 4,
UserResource = 100
};
diff --git a/src/gui/text/qtextdocument_p.cpp b/src/gui/text/qtextdocument_p.cpp
index c0a0c1a177..0e3c8d0e83 100644
--- a/src/gui/text/qtextdocument_p.cpp
+++ b/src/gui/text/qtextdocument_p.cpp
@@ -40,6 +40,7 @@
#include <private/qtools_p.h>
#include <qdebug.h>
+#include <qscopedvaluerollback.h>
#include "qtextdocument_p.h"
#include "qtextdocument.h"
#include <qtextformat.h>
@@ -274,9 +275,10 @@ void QTextDocumentPrivate::clear()
rtFrame = 0;
init();
cursors = oldCursors;
- inContentsChange = true;
- emit q->contentsChange(0, len, 0);
- inContentsChange = false;
+ {
+ QScopedValueRollback<bool> bg(inContentsChange, true);
+ emit q->contentsChange(0, len, 0);
+ }
if (lout)
lout->documentChanged(0, len, 0);
} QT_CATCH(...) {
@@ -309,9 +311,10 @@ void QTextDocumentPrivate::setLayout(QAbstractTextDocumentLayout *layout)
it->free();
emit q->documentLayoutChanged();
- inContentsChange = true;
- emit q->contentsChange(0, 0, length());
- inContentsChange = false;
+ {
+ QScopedValueRollback<bool> bg(inContentsChange, true);
+ emit q->contentsChange(0, 0, length());
+ }
if (lout)
lout->documentChanged(0, 0, length());
}
@@ -1213,9 +1216,8 @@ void QTextDocumentPrivate::finishEdit()
if (lout && docChangeFrom >= 0) {
if (!inContentsChange) {
- inContentsChange = true;
+ QScopedValueRollback<bool> bg(inContentsChange, true);
emit q->contentsChange(docChangeFrom, docChangeOldLength, docChangeLength);
- inContentsChange = false;
}
lout->documentChanged(docChangeFrom, docChangeOldLength, docChangeLength);
}
diff --git a/src/gui/text/qtextdocument_p.h b/src/gui/text/qtextdocument_p.h
index cad9131fbf..a8e17bfc08 100644
--- a/src/gui/text/qtextdocument_p.h
+++ b/src/gui/text/qtextdocument_p.h
@@ -101,10 +101,10 @@ class QTextBlockData : public QFragment<3>
{
public:
inline void initialize()
- { layout = 0; userData = 0; userState = -1; revision = 0; hidden = 0; }
+ { layout = nullptr; userData = nullptr; userState = -1; revision = 0; hidden = 0; }
void invalidate() const;
inline void free()
- { delete layout; layout = 0; delete userData; userData = 0; }
+ { delete layout; layout = nullptr; delete userData; userData = nullptr; }
mutable int format;
// ##### probably store a QTextEngine * here!
@@ -339,6 +339,7 @@ private:
int lastBlockCount;
public:
+ bool inContentsChange;
QTextOption defaultTextOption;
Qt::CursorMoveStyle defaultCursorMoveStyle;
#ifndef QT_NO_CSSPARSER
@@ -346,7 +347,6 @@ public:
#endif
int maximumBlockCount;
uint needsEnsureMaximumBlockCount : 1;
- uint inContentsChange : 1;
uint blockCursorAdjustment : 1;
QSizeF pageSize;
QString title;
diff --git a/src/gui/text/qtextdocumentfragment.cpp b/src/gui/text/qtextdocumentfragment.cpp
index aef4ea1522..1905d9a1b1 100644
--- a/src/gui/text/qtextdocumentfragment.cpp
+++ b/src/gui/text/qtextdocumentfragment.cpp
@@ -715,6 +715,10 @@ QTextHtmlImporter::ProcessNodeResult QTextHtmlImporter::processSpecialNodes()
case Html_img: {
QTextImageFormat fmt;
fmt.setName(currentNode->imageName);
+ if (!currentNode->text.isEmpty())
+ fmt.setProperty(QTextFormat::ImageTitle, currentNode->text);
+ if (!currentNode->imageAlt.isEmpty())
+ fmt.setProperty(QTextFormat::ImageAltText, currentNode->imageAlt);
fmt.merge(currentNode->charFormat);
diff --git a/src/gui/text/qtextdocumentfragment_p.h b/src/gui/text/qtextdocumentfragment_p.h
index de01a02fbb..67b0c2c600 100644
--- a/src/gui/text/qtextdocumentfragment_p.h
+++ b/src/gui/text/qtextdocumentfragment_p.h
@@ -125,7 +125,7 @@ public:
QTextHtmlImporter(QTextDocument *_doc, const QString &html,
ImportMode mode,
- const QTextDocument *resourceProvider = 0);
+ const QTextDocument *resourceProvider = nullptr);
void import();
@@ -163,7 +163,7 @@ private:
#endif
struct TableCellIterator
{
- inline TableCellIterator(QTextTable *t = 0) : table(t), row(0), column(0) {}
+ inline TableCellIterator(QTextTable *t = nullptr) : table(t), row(0), column(0) {}
inline TableCellIterator &operator++() {
if (atEnd())
@@ -182,7 +182,7 @@ private:
return *this;
}
- inline bool atEnd() const { return table == 0 || row >= table->rows(); }
+ inline bool atEnd() const { return table == nullptr || row >= table->rows(); }
QTextTableCell cell() const { return table->cellAt(row, column); }
diff --git a/src/gui/text/qtextdocumentlayout.cpp b/src/gui/text/qtextdocumentlayout.cpp
index cf02e2f9c8..a1b21b111b 100644
--- a/src/gui/text/qtextdocumentlayout.cpp
+++ b/src/gui/text/qtextdocumentlayout.cpp
@@ -931,7 +931,10 @@ void QTextDocumentLayoutPrivate::drawFrame(const QPointF &offset, QPainter *pain
Q_ASSERT(!fd->sizeDirty);
Q_ASSERT(!fd->layoutDirty);
- const QPointF off = offset + fd->position.toPointF();
+ // floor the offset to avoid painting artefacts when drawing adjacent borders
+ // we later also round table cell heights and widths
+ const QPointF off = QPointF(QPointF(offset + fd->position.toPointF()).toPoint());
+
if (context.clip.isValid()
&& (off.y() > context.clip.bottom() || off.y() + fd->size.height.toReal() < context.clip.top()
|| off.x() > context.clip.right() || off.x() + fd->size.width.toReal() < context.clip.left()))
@@ -1442,6 +1445,21 @@ void QTextDocumentLayoutPrivate::drawListItem(const QPointF &offset, QPainter *p
QBrush brush = context.palette.brush(QPalette::Text);
+ bool marker = bl.blockFormat().marker() != QTextBlockFormat::NoMarker;
+ if (marker) {
+ int adj = fontMetrics.lineSpacing() / 6;
+ r.adjust(-adj, 0, -adj, 0);
+ if (bl.blockFormat().marker() == QTextBlockFormat::Checked) {
+ // ### Qt6: render with QStyle / PE_IndicatorCheckBox. We don't currently
+ // have access to that here, because it would be a widget dependency.
+ painter->setPen(QPen(painter->pen().color(), 2));
+ painter->drawLine(r.topLeft(), r.bottomRight());
+ painter->drawLine(r.topRight(), r.bottomLeft());
+ painter->setPen(QPen(painter->pen().color(), 0));
+ }
+ painter->drawRect(r.adjusted(-adj, -adj, adj, adj));
+ }
+
switch (style) {
case QTextListFormat::ListDecimal:
case QTextListFormat::ListLowerAlpha:
@@ -1462,16 +1480,21 @@ void QTextDocumentLayoutPrivate::drawListItem(const QPointF &offset, QPainter *p
break;
}
case QTextListFormat::ListSquare:
- painter->fillRect(r, brush);
+ if (!marker)
+ painter->fillRect(r, brush);
break;
case QTextListFormat::ListCircle:
- painter->setPen(QPen(brush, 0));
- painter->drawEllipse(r.translated(0.5, 0.5)); // pixel align for sharper rendering
+ if (!marker) {
+ painter->setPen(QPen(brush, 0));
+ painter->drawEllipse(r.translated(0.5, 0.5)); // pixel align for sharper rendering
+ }
break;
case QTextListFormat::ListDisc:
- painter->setBrush(brush);
- painter->setPen(Qt::NoPen);
- painter->drawEllipse(r);
+ if (!marker) {
+ painter->setBrush(brush);
+ painter->setPen(Qt::NoPen);
+ painter->drawEllipse(r);
+ }
break;
case QTextListFormat::ListStyleUndefined:
break;
@@ -1597,7 +1620,7 @@ QRectF QTextDocumentLayoutPrivate::layoutTable(QTextTable *table, int layoutFrom
for (int i = 0; i < children.count(); ++i) {
QTextFrame *frame = children.at(i);
QTextTableCell cell = table->cellAt(frame->firstPosition());
- td->childFrameMap.insertMulti(cell.row() + cell.column() * rows, frame);
+ td->childFrameMap.insert(cell.row() + cell.column() * rows, frame);
}
}
@@ -1667,7 +1690,8 @@ recalc_minmax_widths:
for (int n = 0; n < cspan; ++n) {
const int col = i + n;
QFixed w = widthToDistribute / (cspan - n);
- td->minWidths[col] = qMax(td->minWidths.at(col), w);
+ // ceil to avoid going below minWidth when rounding all column widths later
+ td->minWidths[col] = qMax(td->minWidths.at(col), w).ceil();
widthToDistribute -= td->minWidths.at(col);
if (widthToDistribute <= 0)
break;
@@ -1773,6 +1797,18 @@ recalc_minmax_widths:
}
}
+ // in order to get a correct border rendering we must ensure that the distance between
+ // two cells is exactly 2 * td->border pixel. we do this by rounding the calculated width
+ // values here.
+ // to minimize the total rounding error we propagate the rounding error for each width
+ // to its successor.
+ QFixed error = 0;
+ for (int i = 0; i < columns; ++i) {
+ QFixed orig = td->widths[i];
+ td->widths[i] = (td->widths[i] - error).round();
+ error = td->widths[i] - orig;
+ }
+
td->columnPositions.resize(columns);
td->columnPositions[0] = leftMargin /*includes table border*/ + cellSpacing + td->border;
@@ -1873,7 +1909,7 @@ relayout:
if (cellRow != r) {
// the last row gets all the remaining space
if (cellRow + rspan - 1 == r)
- td->heights[r] = qMax(td->heights.at(r), heightToDistribute.at(c) - dropDistance);
+ td->heights[r] = qMax(td->heights.at(r), heightToDistribute.at(c) - dropDistance).round();
continue;
}
}
@@ -1894,7 +1930,7 @@ relayout:
td, absoluteTableY,
/*withPageBreaks =*/true);
- const QFixed height = layoutStruct.y + bottomPadding + topPadding;
+ const QFixed height = (layoutStruct.y + bottomPadding + topPadding).round();
if (rspan > 1)
heightToDistribute[c] = height + dropDistance;
diff --git a/src/gui/text/qtextdocumentwriter.cpp b/src/gui/text/qtextdocumentwriter.cpp
index 42e623153a..193d2c0dd3 100644
--- a/src/gui/text/qtextdocumentwriter.cpp
+++ b/src/gui/text/qtextdocumentwriter.cpp
@@ -51,6 +51,9 @@
#include "qtextdocumentfragment_p.h"
#include "qtextodfwriter_p.h"
+#if QT_CONFIG(textmarkdownwriter)
+#include "qtextmarkdownwriter_p.h"
+#endif
#include <algorithm>
@@ -267,6 +270,18 @@ bool QTextDocumentWriter::write(const QTextDocument *document)
}
#endif // QT_NO_TEXTODFWRITER
+#if QT_CONFIG(textmarkdownwriter)
+ if (format == "md" || format == "mkd" || format == "markdown") {
+ if (!d->device->isWritable() && !d->device->open(QIODevice::WriteOnly)) {
+ qWarning("QTextDocumentWriter::write: the device can not be opened for writing");
+ return false;
+ }
+ QTextStream s(d->device);
+ QTextMarkdownWriter writer(s, QTextDocument::MarkdownDialectGitHub);
+ return writer.writeAll(document);
+ }
+#endif // textmarkdownwriter
+
#ifndef QT_NO_TEXTHTMLPARSER
if (format == "html" || format == "htm") {
if (!d->device->isWritable() && ! d->device->open(QIODevice::WriteOnly)) {
@@ -348,6 +363,7 @@ QTextCodec *QTextDocumentWriter::codec() const
\header \li Format \li Description
\row \li plaintext \li Plain text
\row \li HTML \li HyperText Markup Language
+ \row \li markdown \li Markdown (CommonMark or GitHub dialects)
\row \li ODF \li OpenDocument Format
\endtable
@@ -364,6 +380,9 @@ QList<QByteArray> QTextDocumentWriter::supportedDocumentFormats()
#ifndef QT_NO_TEXTODFWRITER
answer << "ODF";
#endif // QT_NO_TEXTODFWRITER
+#if QT_CONFIG(textmarkdownwriter)
+ answer << "markdown";
+#endif
std::sort(answer.begin(), answer.end());
return answer;
diff --git a/src/gui/text/qtextengine.cpp b/src/gui/text/qtextengine.cpp
index 2da13289bf..c267ade0c2 100644
--- a/src/gui/text/qtextengine.cpp
+++ b/src/gui/text/qtextengine.cpp
@@ -990,7 +990,7 @@ struct QBidiAlgorithm {
BIDI_DEBUG() << "before implicit level processing:";
IsolatedRunSequenceIterator it(runs, i);
while (!it.atEnd()) {
- BIDI_DEBUG() << " " << *it << hex << text[*it].unicode() << analysis[*it].bidiDirection;
+ BIDI_DEBUG() << " " << *it << Qt::hex << text[*it].unicode() << analysis[*it].bidiDirection;
++it;
}
}
@@ -1003,7 +1003,7 @@ struct QBidiAlgorithm {
BIDI_DEBUG() << "after W4/W5";
IsolatedRunSequenceIterator it(runs, i);
while (!it.atEnd()) {
- BIDI_DEBUG() << " " << *it << hex << text[*it].unicode() << analysis[*it].bidiDirection;
+ BIDI_DEBUG() << " " << *it << Qt::hex << text[*it].unicode() << analysis[*it].bidiDirection;
++it;
}
}
@@ -1089,7 +1089,7 @@ struct QBidiAlgorithm {
if (BidiDebugEnabled) {
BIDI_DEBUG() << ">>>> start bidi, text length" << length;
for (int i = 0; i < length; ++i)
- BIDI_DEBUG() << hex << " (" << i << ")" << text[i].unicode() << text[i].direction();
+ BIDI_DEBUG() << Qt::hex << " (" << i << ")" << text[i].unicode() << text[i].direction();
}
{
@@ -1158,7 +1158,7 @@ struct QBidiAlgorithm {
if (BidiDebugEnabled) {
BIDI_DEBUG() << "final resolved levels:";
for (int i = 0; i < length; ++i)
- BIDI_DEBUG() << " " << i << hex << text[i].unicode() << dec << (int)analysis[i].bidiLevel;
+ BIDI_DEBUG() << " " << i << Qt::hex << text[i].unicode() << Qt::dec << (int)analysis[i].bidiLevel;
}
return true;
@@ -1969,7 +1969,9 @@ const QCharAttributes *QTextEngine::attributes() const
QUnicodeTools::initCharAttributes(reinterpret_cast<const ushort *>(layoutData->string.constData()),
layoutData->string.length(),
scriptItems.data(), scriptItems.size(),
- (QCharAttributes *)layoutData->memory);
+ (QCharAttributes *)layoutData->memory,
+ QUnicodeTools::CharAttributeOptions(QUnicodeTools::DefaultOptionsCompat
+ | QUnicodeTools::HangulLineBreakTailoring));
layoutData->haveCharAttributes = true;
diff --git a/src/gui/text/qtextengine_p.h b/src/gui/text/qtextengine_p.h
index e9187ea605..fddda7f2f8 100644
--- a/src/gui/text/qtextengine_p.h
+++ b/src/gui/text/qtextengine_p.h
@@ -304,8 +304,8 @@ class QTextItemInt : public QTextItem
{
public:
inline QTextItemInt()
- : justified(false), underlineStyle(QTextCharFormat::NoUnderline), num_chars(0), chars(0),
- logClusters(0), f(0), fontEngine(0)
+ : justified(false), underlineStyle(QTextCharFormat::NoUnderline), num_chars(0), chars(nullptr),
+ logClusters(nullptr), f(nullptr), fontEngine(nullptr)
{}
QTextItemInt(const QScriptItem &si, QFont *font, const QTextCharFormat &format = QTextCharFormat());
QTextItemInt(const QGlyphLayout &g, QFont *font, const QChar *chars, int numChars, QFontEngine *fe,
@@ -335,7 +335,7 @@ public:
struct QScriptItem
{
- Q_DECL_CONSTEXPR QScriptItem(int p, QScriptAnalysis a) Q_DECL_NOTHROW
+ Q_DECL_CONSTEXPR QScriptItem(int p, QScriptAnalysis a) noexcept
: position(p), analysis(a),
num_glyphs(0), descent(-1), ascent(-1), leading(-1), width(-1),
glyph_data_offset(0) {}
@@ -348,7 +348,7 @@ struct QScriptItem
QFixed leading;
QFixed width;
int glyph_data_offset;
- Q_DECL_CONSTEXPR QFixed height() const Q_DECL_NOTHROW { return ascent + descent; }
+ Q_DECL_CONSTEXPR QFixed height() const noexcept { return ascent + descent; }
private:
friend class QVector<QScriptItem>;
QScriptItem() {}; // for QVector, don't use
@@ -484,7 +484,7 @@ public:
return end - si->position;
}
- QFontEngine *fontEngine(const QScriptItem &si, QFixed *ascent = 0, QFixed *descent = 0, QFixed *leading = 0) const;
+ QFontEngine *fontEngine(const QScriptItem &si, QFixed *ascent = nullptr, QFixed *descent = nullptr, QFixed *leading = nullptr) const;
QFont font(const QScriptItem &si) const;
inline QFont font() const { return fnt; }
@@ -530,7 +530,7 @@ public:
inline QTextFormatCollection *formatCollection() const {
if (block.docHandle())
return block.docHandle()->formatCollection();
- return specialData ? specialData->formatCollection.data() : 0;
+ return specialData ? specialData->formatCollection.data() : nullptr;
}
QTextCharFormat format(const QScriptItem *si) const;
inline QAbstractTextDocumentLayout *docLayout() const {
@@ -553,8 +553,8 @@ private:
mutable int prevPosition;
mutable int prevLength;
inline void reset() {
- prevFontEngine = 0;
- prevScaledFontEngine = 0;
+ prevFontEngine = nullptr;
+ prevScaledFontEngine = nullptr;
prevScript = -1;
prevPosition = -1;
prevLength = -1;
@@ -684,7 +684,7 @@ Q_DECLARE_TYPEINFO(QTextEngine::ItemDecoration, Q_MOVABLE_TYPE);
struct QTextLineItemIterator
{
QTextLineItemIterator(QTextEngine *eng, int lineNum, const QPointF &pos = QPointF(),
- const QTextLayout::FormatRange *_selection = 0);
+ const QTextLayout::FormatRange *_selection = nullptr);
inline bool atEnd() const { return logicalItem >= nItems - 1; }
inline bool atBeginning() const { return logicalItem <= 0; }
diff --git a/src/gui/text/qtextformat.cpp b/src/gui/text/qtextformat.cpp
index 4a2985f74c..090c6cc4ce 100644
--- a/src/gui/text/qtextformat.cpp
+++ b/src/gui/text/qtextformat.cpp
@@ -564,6 +564,18 @@ Q_GUI_EXPORT QDataStream &operator>>(QDataStream &stream, QTextFormat &fmt)
\value BlockTrailingHorizontalRulerWidth The width of a horizontal ruler element.
\value HeadingLevel The level of a heading, for example 1 corresponds to an HTML H1 tag; otherwise 0.
This enum value has been added in Qt 5.12.
+ \value BlockCodeFence The character that was used in the "fences" around a Markdown code block.
+ If the code block was indented rather than fenced, the block should not have this property.
+ This enum value has been added in Qt 5.14.
+
+ \value BlockQuoteLevel The depth of nested quoting on this block: 1 means the block is a top-level block quote.
+ Blocks that are not block quotes should not have this property.
+ This enum value has been added in Qt 5.14.
+ \value BlockCodeLanguage The programming language in a preformatted or code block.
+ Blocks that do not contain code should not have this property.
+ This enum value has been added in Qt 5.14.
+ \value BlockMarker The \l{QTextBlockFormat::MarkerType}{type of adornment} to be shown alongside the block.
+ This enum value has been added in Qt 5.14.
Character properties
@@ -650,7 +662,13 @@ Q_GUI_EXPORT QDataStream &operator>>(QDataStream &stream, QTextFormat &fmt)
Image properties
- \value ImageName
+ \value ImageName The filename or source of the image.
+ \value ImageTitle The title attribute of an HTML image tag, or
+ the quoted string that comes after the URL in a Markdown image link.
+ This enum value has been added in Qt 5.14.
+ \value ImageAltText The alt attribute of an HTML image tag, or
+ the image description in a Markdown image link.
+ This enum value has been added in Qt 5.14.
\value ImageWidth
\value ImageHeight
\value ImageQuality
@@ -2329,6 +2347,50 @@ QList<QTextOption::Tab> QTextBlockFormat::tabPositions() const
/*!
+ \fn void QTextBlockFormat::setMarker(MarkerType marker)
+ \since 5.14
+
+ Sets the type of adornment that should be rendered alongside the paragraph to \a marker.
+ For example, a list item can be adorned with a checkbox, either checked
+ or unchecked, as a replacement for its bullet. The default is \c NoMarker.
+
+ \sa marker()
+*/
+
+
+/*!
+ \fn MarkerType QTextBlockFormat::marker() const
+ \since 5.14
+
+ Returns the paragraph's marker if one has been set, or \c NoMarker if not.
+
+ \sa setMarker()
+*/
+
+
+/*!
+ \since 5.14
+ \enum QTextBlockFormat::MarkerType
+
+ This enum describes the types of markers a list item can have.
+ If a list item (a paragraph for which \l QTextBlock::textList() returns the list)
+ has a marker, it is rendered instead of the normal bullet.
+ In this way, checkable list items can be mixed with plain list items in the
+ same list, overriding the type of bullet specified by the
+ \l QTextListFormat::style() for the entire list.
+
+ \value NoMarker This is the default: the list item's bullet will be shown.
+ \value Unchecked Instead of the list item's bullet, an unchecked checkbox will be shown.
+ \value Checked Instead of the list item's bullet, a checked checkbox will be shown.
+
+ In the future, this may be extended to specify other types of paragraph
+ decorations.
+
+ \sa QTextListFormat::style()
+*/
+
+
+/*!
\fn void QTextBlockFormat::setLineHeight(qreal height, int heightType)
\since 4.8
diff --git a/src/gui/text/qtextformat.h b/src/gui/text/qtextformat.h
index 80d8e82694..a91461dcae 100644
--- a/src/gui/text/qtextformat.h
+++ b/src/gui/text/qtextformat.h
@@ -176,6 +176,10 @@ public:
BlockNonBreakableLines = 0x1050,
BlockTrailingHorizontalRulerWidth = 0x1060,
HeadingLevel = 0x1070,
+ BlockQuoteLevel = 0x1080,
+ BlockCodeLanguage = 0x1090,
+ BlockCodeFence = 0x1091,
+ BlockMarker = 0x10A0,
// character properties
FirstFontProperty = 0x1FE0,
@@ -250,6 +254,8 @@ public:
// image properties
ImageName = 0x5000,
+ ImageTitle = 0x5001,
+ ImageAltText = 0x5002,
ImageWidth = 0x5010,
ImageHeight = 0x5011,
ImageQuality = 0x5014,
@@ -605,6 +611,12 @@ public:
LineDistanceHeight = 4
};
+ enum MarkerType {
+ NoMarker = 0,
+ Unchecked = 1,
+ Checked = 2
+ };
+
QTextBlockFormat();
bool isValid() const { return isBlockFormat(); }
@@ -668,6 +680,11 @@ public:
void setTabPositions(const QList<QTextOption::Tab> &tabs);
QList<QTextOption::Tab> tabPositions() const;
+ inline void setMarker(MarkerType marker)
+ { setProperty(BlockMarker, int(marker)); }
+ inline MarkerType marker() const
+ { return MarkerType(intProperty(BlockMarker)); }
+
protected:
explicit QTextBlockFormat(const QTextFormat &fmt);
friend class QTextFormat;
diff --git a/src/gui/text/qtexthtmlparser.cpp b/src/gui/text/qtexthtmlparser.cpp
index 895232e4c7..642f0893b4 100644
--- a/src/gui/text/qtexthtmlparser.cpp
+++ b/src/gui/text/qtexthtmlparser.cpp
@@ -1125,6 +1125,7 @@ void QTextHtmlParserNode::initializeProperties(const QTextHtmlParserNode *parent
margin[QTextHtmlParser::MarginBottom] = 12;
margin[QTextHtmlParser::MarginLeft] = 40;
margin[QTextHtmlParser::MarginRight] = 40;
+ blockFormat.setProperty(QTextFormat::BlockQuoteLevel, 1);
break;
case Html_dl:
margin[QTextHtmlParser::MarginTop] = 8;
@@ -1555,6 +1556,10 @@ void QTextHtmlParser::applyAttributes(const QStringList &attributes)
} else if (key == QLatin1String("height")) {
node->imageHeight = -2; // register that there is a value for it.
setFloatAttribute(&node->imageHeight, value);
+ } else if (key == QLatin1String("alt")) {
+ node->imageAlt = value;
+ } else if (key == QLatin1String("title")) {
+ node->text = value;
}
break;
case Html_tr:
@@ -1630,6 +1635,10 @@ void QTextHtmlParser::applyAttributes(const QStringList &attributes)
else if (key == QLatin1String("type"))
linkType = value;
break;
+ case Html_pre:
+ if (key == QLatin1String("class") && value.startsWith(QLatin1String("language-")))
+ node->blockFormat.setProperty(QTextFormat::BlockCodeLanguage, value.mid(9));
+ break;
default:
break;
}
diff --git a/src/gui/text/qtexthtmlparser_p.h b/src/gui/text/qtexthtmlparser_p.h
index 73dac38b82..6ce294d211 100644
--- a/src/gui/text/qtexthtmlparser_p.h
+++ b/src/gui/text/qtexthtmlparser_p.h
@@ -184,6 +184,7 @@ struct QTextHtmlParserNode {
QString textListNumberPrefix;
QString textListNumberSuffix;
QString imageName;
+ QString imageAlt;
qreal imageWidth;
qreal imageHeight;
QTextLength width;
diff --git a/src/gui/text/qtextimagehandler_p.h b/src/gui/text/qtextimagehandler_p.h
index 339ef0af4f..fafd394ad3 100644
--- a/src/gui/text/qtextimagehandler_p.h
+++ b/src/gui/text/qtextimagehandler_p.h
@@ -65,7 +65,7 @@ class Q_GUI_EXPORT QTextImageHandler : public QObject,
Q_OBJECT
Q_INTERFACES(QTextObjectInterface)
public:
- explicit QTextImageHandler(QObject *parent = 0);
+ explicit QTextImageHandler(QObject *parent = nullptr);
virtual QSizeF intrinsicSize(QTextDocument *doc, int posInDocument, const QTextFormat &format) override;
virtual void drawObject(QPainter *p, const QRectF &rect, QTextDocument *doc, int posInDocument, const QTextFormat &format) override;
diff --git a/src/gui/text/qtextmarkdownimporter.cpp b/src/gui/text/qtextmarkdownimporter.cpp
new file mode 100644
index 0000000000..b96263f5fc
--- /dev/null
+++ b/src/gui/text/qtextmarkdownimporter.cpp
@@ -0,0 +1,568 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtextmarkdownimporter_p.h"
+#include "qtextdocumentfragment_p.h"
+#include <QLoggingCategory>
+#if QT_CONFIG(regularexpression)
+#include <QRegularExpression>
+#endif
+#include <QTextCursor>
+#include <QTextDocument>
+#include <QTextDocumentFragment>
+#include <QTextList>
+#include <QTextTable>
+#include "../../3rdparty/md4c/md4c.h"
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcMD, "qt.text.markdown")
+
+static const QChar Newline = QLatin1Char('\n');
+static const QChar Space = QLatin1Char(' ');
+
+// 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
+
+// --------------------------------------------------------
+// MD4C callback function wrappers
+
+static int CbEnterBlock(MD_BLOCKTYPE type, void *detail, void *userdata)
+{
+ QTextMarkdownImporter *mdi = static_cast<QTextMarkdownImporter *>(userdata);
+ return mdi->cbEnterBlock(int(type), detail);
+}
+
+static int CbLeaveBlock(MD_BLOCKTYPE type, void *detail, void *userdata)
+{
+ QTextMarkdownImporter *mdi = static_cast<QTextMarkdownImporter *>(userdata);
+ return mdi->cbLeaveBlock(int(type), detail);
+}
+
+static int CbEnterSpan(MD_SPANTYPE type, void *detail, void *userdata)
+{
+ QTextMarkdownImporter *mdi = static_cast<QTextMarkdownImporter *>(userdata);
+ return mdi->cbEnterSpan(int(type), detail);
+}
+
+static int CbLeaveSpan(MD_SPANTYPE type, void *detail, void *userdata)
+{
+ QTextMarkdownImporter *mdi = static_cast<QTextMarkdownImporter *>(userdata);
+ return mdi->cbLeaveSpan(int(type), detail);
+}
+
+static int CbText(MD_TEXTTYPE type, const MD_CHAR *text, MD_SIZE size, void *userdata)
+{
+ QTextMarkdownImporter *mdi = static_cast<QTextMarkdownImporter *>(userdata);
+ return mdi->cbText(int(type), text, size);
+}
+
+static void CbDebugLog(const char *msg, void *userdata)
+{
+ Q_UNUSED(userdata)
+ qCDebug(lcMD) << msg;
+}
+
+// MD4C callback function wrappers
+// --------------------------------------------------------
+
+static Qt::Alignment MdAlignment(MD_ALIGN a, Qt::Alignment defaultAlignment = Qt::AlignLeft | Qt::AlignVCenter)
+{
+ switch (a) {
+ case MD_ALIGN_LEFT:
+ return Qt::AlignLeft | Qt::AlignVCenter;
+ case MD_ALIGN_CENTER:
+ return Qt::AlignHCenter | Qt::AlignVCenter;
+ case MD_ALIGN_RIGHT:
+ return Qt::AlignRight | Qt::AlignVCenter;
+ default: // including MD_ALIGN_DEFAULT
+ return defaultAlignment;
+ }
+}
+
+QTextMarkdownImporter::QTextMarkdownImporter(QTextMarkdownImporter::Features features)
+ : m_monoFont(QFontDatabase::systemFont(QFontDatabase::FixedFont))
+ , m_features(features)
+{
+}
+
+void QTextMarkdownImporter::import(QTextDocument *doc, const QString &markdown)
+{
+ MD_PARSER callbacks = {
+ 0, // abi_version
+ unsigned(m_features),
+ &CbEnterBlock,
+ &CbLeaveBlock,
+ &CbEnterSpan,
+ &CbLeaveSpan,
+ &CbText,
+ &CbDebugLog,
+ nullptr // syntax
+ };
+ m_doc = doc;
+ m_paragraphMargin = m_doc->defaultFont().pointSize() * 2 / 3;
+ m_cursor = new QTextCursor(doc);
+ doc->clear();
+ 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;
+}
+
+int QTextMarkdownImporter::cbEnterBlock(int blockType, void *det)
+{
+ m_blockType = blockType;
+ 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", m_listStack.count());
+ else
+ qCDebug(lcMD, "P");
+ m_needsInsertBlock = true;
+ break;
+ case MD_BLOCK_QUOTE:
+ ++m_blockQuoteDepth;
+ qCDebug(lcMD, "QUOTE level %d", m_blockQuoteDepth);
+ break;
+ case MD_BLOCK_CODE: {
+ MD_BLOCK_CODE_DETAIL *detail = static_cast<MD_BLOCK_CODE_DETAIL *>(det);
+ m_codeBlock = true;
+ m_blockCodeLanguage = QLatin1String(detail->lang.text, int(detail->lang.size));
+ m_blockCodeFence = detail->fence_char;
+ QString info = QLatin1String(detail->info.text, int(detail->info.size));
+ m_needsInsertBlock = true;
+ if (m_blockQuoteDepth)
+ qCDebug(lcMD, "CODE lang '%s' info '%s' fenced with '%c' inside QUOTE %d", qPrintable(m_blockCodeLanguage), qPrintable(info), m_blockCodeFence, m_blockQuoteDepth);
+ else
+ qCDebug(lcMD, "CODE lang '%s' info '%s' fenced with '%c'", qPrintable(m_blockCodeLanguage), qPrintable(info), m_blockCodeFence);
+ } break;
+ case MD_BLOCK_H: {
+ MD_BLOCK_H_DETAIL *detail = static_cast<MD_BLOCK_H_DETAIL *>(det);
+ QTextBlockFormat blockFmt;
+ QTextCharFormat charFmt;
+ int sizeAdjustment = 4 - int(detail->level); // H1 to H6: +3 to -2
+ charFmt.setProperty(QTextFormat::FontSizeAdjustment, sizeAdjustment);
+ charFmt.setFontWeight(QFont::Bold);
+ blockFmt.setHeadingLevel(int(detail->level));
+ m_needsInsertBlock = false;
+ m_cursor->insertBlock(blockFmt, charFmt);
+ qCDebug(lcMD, "H%d", detail->level);
+ } break;
+ case MD_BLOCK_LI: {
+ m_needsInsertBlock = true;
+ m_listItem = true;
+ MD_BLOCK_LI_DETAIL *detail = static_cast<MD_BLOCK_LI_DETAIL *>(det);
+ m_markerType = detail->is_task ?
+ (detail->task_mark == ' ' ? QTextBlockFormat::Unchecked : QTextBlockFormat::Checked) :
+ QTextBlockFormat::NoMarker;
+ qCDebug(lcMD) << "LI";
+ } break;
+ case MD_BLOCK_UL: {
+ MD_BLOCK_UL_DETAIL *detail = static_cast<MD_BLOCK_UL_DETAIL *>(det);
+ m_listFormat = QTextListFormat();
+ m_listFormat.setIndent(m_listStack.count() + 1);
+ switch (detail->mark) {
+ case '*':
+ m_listFormat.setStyle(QTextListFormat::ListCircle);
+ break;
+ case '+':
+ m_listFormat.setStyle(QTextListFormat::ListSquare);
+ break;
+ default: // including '-'
+ m_listFormat.setStyle(QTextListFormat::ListDisc);
+ break;
+ }
+ qCDebug(lcMD, "UL %c level %d", detail->mark, m_listStack.count());
+ m_needsInsertList = true;
+ } break;
+ case MD_BLOCK_OL: {
+ MD_BLOCK_OL_DETAIL *detail = static_cast<MD_BLOCK_OL_DETAIL *>(det);
+ m_listFormat = QTextListFormat();
+ m_listFormat.setIndent(m_listStack.count() + 1);
+ m_listFormat.setNumberSuffix(QChar::fromLatin1(detail->mark_delimiter));
+ m_listFormat.setStyle(QTextListFormat::ListDecimal);
+ qCDebug(lcMD, "OL xx%d level %d", detail->mark_delimiter, m_listStack.count());
+ m_needsInsertList = true;
+ } break;
+ case MD_BLOCK_TD: {
+ MD_BLOCK_TD_DETAIL *detail = static_cast<MD_BLOCK_TD_DETAIL *>(det);
+ ++m_tableCol;
+ // absolute movement (and storage of m_tableCol) shouldn't be necessary, but
+ // movePosition(QTextCursor::NextCell) doesn't work
+ QTextTableCell cell = m_currentTable->cellAt(m_tableRowCount - 1, m_tableCol);
+ if (!cell.isValid()) {
+ qWarning("malformed table in Markdown input");
+ return 1;
+ }
+ *m_cursor = cell.firstCursorPosition();
+ QTextBlockFormat blockFmt = m_cursor->blockFormat();
+ blockFmt.setAlignment(MdAlignment(detail->align));
+ m_cursor->setBlockFormat(blockFmt);
+ qCDebug(lcMD) << "TD; align" << detail->align << MdAlignment(detail->align) << "col" << m_tableCol;
+ } break;
+ case MD_BLOCK_TH: {
+ ++m_tableColumnCount;
+ ++m_tableCol;
+ if (m_currentTable->columns() < m_tableColumnCount)
+ m_currentTable->appendColumns(1);
+ auto cell = m_currentTable->cellAt(m_tableRowCount - 1, m_tableCol);
+ if (!cell.isValid()) {
+ qWarning("malformed table in Markdown input");
+ return 1;
+ }
+ auto fmt = cell.format();
+ fmt.setFontWeight(QFont::Bold);
+ cell.setFormat(fmt);
+ } break;
+ case MD_BLOCK_TR: {
+ ++m_tableRowCount;
+ m_nonEmptyTableCells.clear();
+ if (m_currentTable->rows() < m_tableRowCount)
+ m_currentTable->appendRows(1);
+ m_tableCol = -1;
+ qCDebug(lcMD) << "TR" << m_currentTable->rows();
+ } break;
+ case MD_BLOCK_TABLE:
+ m_tableColumnCount = 0;
+ m_tableRowCount = 0;
+ 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());
+ } break;
+ default:
+ break; // nothing to do for now
+ }
+ return 0; // no error
+}
+
+int QTextMarkdownImporter::cbLeaveBlock(int blockType, void *detail)
+{
+ Q_UNUSED(detail)
+ switch (blockType) {
+ case MD_BLOCK_P:
+ m_listItem = false;
+ break;
+ case MD_BLOCK_UL:
+ case MD_BLOCK_OL:
+ qCDebug(lcMD, "list at level %d ended", m_listStack.count());
+ m_listStack.pop();
+ break;
+ case MD_BLOCK_TR: {
+ // https://github.com/mity/md4c/issues/29
+ // MD4C doesn't tell us explicitly which cells are merged, so merge empty cells
+ // with previous non-empty ones
+ int mergeEnd = -1;
+ int mergeBegin = -1;
+ for (int col = m_tableCol; col >= 0; --col) {
+ if (m_nonEmptyTableCells.contains(col)) {
+ if (mergeEnd >= 0 && mergeBegin >= 0) {
+ qCDebug(lcMD) << "merging cells" << mergeBegin << "to" << mergeEnd << "inclusive, on row" << m_currentTable->rows() - 1;
+ m_currentTable->mergeCells(m_currentTable->rows() - 1, mergeBegin - 1, 1, mergeEnd - mergeBegin + 2);
+ }
+ mergeEnd = -1;
+ mergeBegin = -1;
+ } else {
+ if (mergeEnd < 0)
+ mergeEnd = col;
+ else
+ mergeBegin = col;
+ }
+ }
+ } break;
+ case MD_BLOCK_QUOTE: {
+ qCDebug(lcMD, "QUOTE level %d ended", m_blockQuoteDepth);
+ --m_blockQuoteDepth;
+ m_needsInsertBlock = true;
+ } break;
+ 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);
+ break;
+ case MD_BLOCK_LI:
+ qCDebug(lcMD, "LI at level %d ended", m_listStack.count());
+ m_listItem = false;
+ break;
+ case MD_BLOCK_CODE: {
+ m_codeBlock = false;
+ m_blockCodeLanguage.clear();
+ m_blockCodeFence = 0;
+ if (m_blockQuoteDepth)
+ qCDebug(lcMD, "CODE ended inside QUOTE %d", m_blockQuoteDepth);
+ else
+ qCDebug(lcMD, "CODE ended");
+ m_needsInsertBlock = true;
+ } break;
+ case MD_BLOCK_H:
+ m_cursor->setCharFormat(QTextCharFormat());
+ break;
+ default:
+ break;
+ }
+ return 0; // no error
+}
+
+int QTextMarkdownImporter::cbEnterSpan(int spanType, void *det)
+{
+ QTextCharFormat charFmt;
+ switch (spanType) {
+ case MD_SPAN_EM:
+ charFmt.setFontItalic(true);
+ break;
+ case MD_SPAN_STRONG:
+ charFmt.setFontWeight(QFont::Bold);
+ break;
+ case MD_SPAN_A: {
+ MD_SPAN_A_DETAIL *detail = static_cast<MD_SPAN_A_DETAIL *>(det);
+ QString url = QString::fromLatin1(detail->href.text, int(detail->href.size));
+ QString title = QString::fromLatin1(detail->title.text, int(detail->title.size));
+ charFmt.setAnchorHref(url);
+ charFmt.setAnchorNames(QStringList(title));
+ charFmt.setForeground(m_palette.link());
+ qCDebug(lcMD) << "anchor" << url << title;
+ } break;
+ case MD_SPAN_IMG: {
+ m_imageSpan = true;
+ m_imageFormat = QTextImageFormat();
+ MD_SPAN_IMG_DETAIL *detail = static_cast<MD_SPAN_IMG_DETAIL *>(det);
+ m_imageFormat.setName(QString::fromUtf8(detail->src.text, int(detail->src.size)));
+ m_imageFormat.setProperty(QTextFormat::ImageTitle, QString::fromUtf8(detail->title.text, int(detail->title.size)));
+ break;
+ }
+ case MD_SPAN_CODE:
+ charFmt.setFont(m_monoFont);
+ break;
+ case MD_SPAN_DEL:
+ charFmt.setFontStrikeOut(true);
+ break;
+ }
+ m_spanFormatStack.push(charFmt);
+ qCDebug(lcMD) << spanType << "setCharFormat" << charFmt.font().family() << charFmt.fontWeight()
+ << (charFmt.fontItalic() ? "italic" : "") << charFmt.foreground().color().name();
+ m_cursor->setCharFormat(charFmt);
+ return 0; // no error
+}
+
+int QTextMarkdownImporter::cbLeaveSpan(int spanType, void *detail)
+{
+ Q_UNUSED(detail)
+ QTextCharFormat charFmt;
+ if (!m_spanFormatStack.isEmpty()) {
+ m_spanFormatStack.pop();
+ if (!m_spanFormatStack.isEmpty())
+ charFmt = m_spanFormatStack.top();
+ }
+ m_cursor->setCharFormat(charFmt);
+ qCDebug(lcMD) << spanType << "setCharFormat" << charFmt.font().family() << charFmt.fontWeight()
+ << (charFmt.fontItalic() ? "italic" : "") << charFmt.foreground().color().name();
+ if (spanType == int(MD_SPAN_IMG))
+ m_imageSpan = false;
+ return 0; // no error
+}
+
+int QTextMarkdownImporter::cbText(int textType, const char *text, unsigned size)
+{
+ if (m_needsInsertBlock)
+ insertBlock();
+#if QT_CONFIG(regularexpression)
+ static const QRegularExpression openingBracket(QStringLiteral("<[a-zA-Z]"));
+ static const QRegularExpression closingBracket(QStringLiteral("(/>|</)"));
+#endif
+ QString s = QString::fromUtf8(text, int(size));
+
+ switch (textType) {
+ case MD_TEXT_NORMAL:
+#if QT_CONFIG(regularexpression)
+ if (m_htmlTagDepth) {
+ m_htmlAccumulator += s;
+ s = QString();
+ }
+#endif
+ break;
+ case MD_TEXT_NULLCHAR:
+ s = QString(QChar(0xFFFD)); // CommonMark-required replacement for null
+ break;
+ case MD_TEXT_BR:
+ s = QString(Newline);
+ break;
+ case MD_TEXT_SOFTBR:
+ s = QString(Space);
+ break;
+ case MD_TEXT_CODE:
+ // We'll see MD_SPAN_CODE too, which will set the char format, and that's enough.
+ break;
+#if QT_CONFIG(texthtmlparser)
+ case MD_TEXT_ENTITY:
+ m_cursor->insertHtml(s);
+ s = QString();
+ break;
+#endif
+ case MD_TEXT_HTML:
+ // count how many tags are opened and how many are closed
+#if QT_CONFIG(regularexpression) && QT_CONFIG(texthtmlparser)
+ {
+ int startIdx = 0;
+ while ((startIdx = s.indexOf(openingBracket, startIdx)) >= 0) {
+ ++m_htmlTagDepth;
+ startIdx += 2;
+ }
+ startIdx = 0;
+ while ((startIdx = s.indexOf(closingBracket, startIdx)) >= 0) {
+ --m_htmlTagDepth;
+ startIdx += 2;
+ }
+ }
+ m_htmlAccumulator += s;
+ if (!m_htmlTagDepth) { // all open tags are now closed
+ qCDebug(lcMD) << "HTML" << m_htmlAccumulator;
+ m_cursor->insertHtml(m_htmlAccumulator);
+ if (m_spanFormatStack.isEmpty())
+ m_cursor->setCharFormat(QTextCharFormat());
+ else
+ m_cursor->setCharFormat(m_spanFormatStack.top());
+ m_htmlAccumulator = QString();
+ }
+#endif
+ s = QString();
+ break;
+ }
+
+ switch (m_blockType) {
+ case MD_BLOCK_TD:
+ m_nonEmptyTableCells.append(m_tableCol);
+ break;
+ default:
+ break;
+ }
+
+ if (m_imageSpan) {
+ // TODO we don't yet support alt text with formatting, because of the cases where m_cursor
+ // already inserted the text above. Rather need to accumulate it in case we need it here.
+ 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);
+ return 0; // no error
+ }
+
+ if (!s.isEmpty())
+ 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();
+ bfmt.setIndent(0);
+ m_cursor->setBlockFormat(bfmt);
+ }
+ if (lcMD().isEnabled(QtDebugMsg)) {
+ QTextBlockFormat bfmt = m_cursor->blockFormat();
+ QString debugInfo;
+ if (m_cursor->currentList())
+ debugInfo = QLatin1String("in list at depth ") + QString::number(m_cursor->currentList()->format().indent());
+ if (bfmt.hasProperty(QTextFormat::BlockQuoteLevel))
+ debugInfo += QLatin1String("in blockquote at depth ") +
+ QString::number(bfmt.intProperty(QTextFormat::BlockQuoteLevel));
+ if (bfmt.hasProperty(QTextFormat::BlockCodeLanguage))
+ debugInfo += QLatin1String("in a code block");
+ qCDebug(lcMD) << textType << "in block" << m_blockType << s << qPrintable(debugInfo)
+ << "bindent" << bfmt.indent() << "tindent" << bfmt.textIndent()
+ << "margins" << bfmt.leftMargin() << bfmt.topMargin() << bfmt.bottomMargin() << bfmt.rightMargin();
+ }
+ qCDebug(lcMD) << textType << "in block" << m_blockType << s << "in list?" << m_cursor->currentList()
+ << "indent" << m_cursor->blockFormat().indent();
+ return 0; // no error
+}
+
+/*!
+ 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
+ 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
+ come next, and insertBlock() is called just before the actual text
+ insertion, to create a new block with the right formatting.
+*/
+void QTextMarkdownImporter::insertBlock()
+{
+ QTextCharFormat charFormat;
+ if (!m_spanFormatStack.isEmpty())
+ charFormat = m_spanFormatStack.top();
+ QTextBlockFormat blockFormat;
+ if (!m_listStack.isEmpty() && !m_needsInsertList && m_listItem) {
+ QTextList *list = m_listStack.top();
+ blockFormat = list->item(list->count() - 1).blockFormat();
+ }
+ if (m_blockQuoteDepth) {
+ blockFormat.setProperty(QTextFormat::BlockQuoteLevel, m_blockQuoteDepth);
+ blockFormat.setLeftMargin(BlockQuoteIndent * m_blockQuoteDepth);
+ blockFormat.setRightMargin(BlockQuoteIndent);
+ }
+ if (m_codeBlock) {
+ blockFormat.setProperty(QTextFormat::BlockCodeLanguage, m_blockCodeLanguage);
+ if (m_blockCodeFence)
+ blockFormat.setProperty(QTextFormat::BlockCodeFence, QString(QLatin1Char(m_blockCodeFence)));
+ charFormat.setFont(m_monoFont);
+ } else {
+ blockFormat.setTopMargin(m_paragraphMargin);
+ blockFormat.setBottomMargin(m_paragraphMargin);
+ }
+ if (m_markerType == QTextBlockFormat::NoMarker)
+ blockFormat.clearProperty(QTextFormat::BlockMarker);
+ else
+ blockFormat.setMarker(m_markerType);
+ if (!m_listStack.isEmpty())
+ blockFormat.setIndent(m_listStack.count());
+ m_cursor->insertBlock(blockFormat, charFormat);
+ if (m_needsInsertList) {
+ m_listStack.push(m_cursor->createList(m_listFormat));
+ } else if (!m_listStack.isEmpty() && m_listItem) {
+ m_listStack.top()->add(m_cursor->block());
+ }
+ m_needsInsertList = false;
+ m_needsInsertBlock = false;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/text/qtextmarkdownimporter_p.h b/src/gui/text/qtextmarkdownimporter_p.h
new file mode 100644
index 0000000000..1b8c2ca354
--- /dev/null
+++ b/src/gui/text/qtextmarkdownimporter_p.h
@@ -0,0 +1,143 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTEXTMARKDOWNIMPORTER_H
+#define QTEXTMARKDOWNIMPORTER_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/qfont.h>
+#include <QtGui/qtguiglobal.h>
+#include <QtGui/qpalette.h>
+#include <QtGui/qtextlist.h>
+#include <QtCore/qstack.h>
+
+QT_BEGIN_NAMESPACE
+
+class QTextCursor;
+class QTextDocument;
+class QTextTable;
+
+class Q_GUI_EXPORT QTextMarkdownImporter
+{
+public:
+ enum Feature {
+ // Must be kept in sync with MD_FLAG_* in md4c.h
+ FeatureCollapseWhitespace = 0x0001, // MD_FLAG_COLLAPSEWHITESPACE
+ FeaturePermissiveATXHeaders = 0x0002, // MD_FLAG_PERMISSIVEATXHEADERS
+ FeaturePermissiveURLAutoLinks = 0x0004, // MD_FLAG_PERMISSIVEURLAUTOLINKS
+ FeaturePermissiveMailAutoLinks = 0x0008, // MD_FLAG_PERMISSIVEEMAILAUTOLINKS
+ FeatureNoIndentedCodeBlocks = 0x0010, // MD_FLAG_NOINDENTEDCODEBLOCKS
+ FeatureNoHTMLBlocks = 0x0020, // MD_FLAG_NOHTMLBLOCKS
+ FeatureNoHTMLSpans = 0x0040, // MD_FLAG_NOHTMLSPANS
+ FeatureTables = 0x0100, // MD_FLAG_TABLES
+ FeatureStrikeThrough = 0x0200, // MD_FLAG_STRIKETHROUGH
+ FeaturePermissiveWWWAutoLinks = 0x0400, // MD_FLAG_PERMISSIVEWWWAUTOLINKS
+ FeatureTasklists = 0x0800, // MD_FLAG_TASKLISTS
+ // composite flags
+ FeaturePermissiveAutoLinks = FeaturePermissiveMailAutoLinks | FeaturePermissiveURLAutoLinks | FeaturePermissiveWWWAutoLinks, // MD_FLAG_PERMISSIVEAUTOLINKS
+ FeatureNoHTML = FeatureNoHTMLBlocks | FeatureNoHTMLSpans, // MD_FLAG_NOHTML
+ DialectCommonMark = 0, // MD_DIALECT_COMMONMARK
+ DialectGitHub = FeaturePermissiveAutoLinks | FeatureTables | FeatureStrikeThrough | FeatureTasklists // MD_DIALECT_GITHUB
+ };
+ Q_DECLARE_FLAGS(Features, Feature)
+
+ QTextMarkdownImporter(Features features);
+
+ void import(QTextDocument *doc, const QString &markdown);
+
+public:
+ // MD4C callbacks
+ int cbEnterBlock(int blockType, void* detail);
+ int cbLeaveBlock(int blockType, void* detail);
+ int cbEnterSpan(int spanType, void* detail);
+ int cbLeaveSpan(int spanType, void* detail);
+ int cbText(int textType, const char* text, unsigned size);
+
+private:
+ void insertBlock();
+
+private:
+ QTextDocument *m_doc = nullptr;
+ QTextCursor *m_cursor = nullptr;
+ QTextTable *m_currentTable = nullptr; // because m_cursor->currentTable() doesn't work
+#if QT_CONFIG(regularexpression)
+ QString m_htmlAccumulator;
+#endif
+ QString m_blockCodeLanguage;
+ QVector<int> m_nonEmptyTableCells; // in the current row
+ QStack<QTextList *> m_listStack;
+ QStack<QTextCharFormat> m_spanFormatStack;
+ QFont m_monoFont;
+ QPalette m_palette;
+#if QT_CONFIG(regularexpression)
+ int m_htmlTagDepth = 0;
+#endif
+ int m_blockQuoteDepth = 0;
+ int m_tableColumnCount = 0;
+ int m_tableRowCount = 0;
+ int m_tableCol = -1; // because relative cell movements (e.g. m_cursor->movePosition(QTextCursor::NextCell)) don't work
+ int m_paragraphMargin = 0;
+ int m_blockType = 0;
+ char m_blockCodeFence = 0;
+ Features m_features;
+ QTextImageFormat m_imageFormat;
+ QTextListFormat m_listFormat;
+ QTextBlockFormat::MarkerType m_markerType = QTextBlockFormat::NoMarker;
+ bool m_needsInsertBlock = false;
+ bool m_needsInsertList = false;
+ bool m_listItem = false; // true from the beginning of LI to the end of the first P
+ bool m_codeBlock = false;
+ bool m_imageSpan = false;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QTextMarkdownImporter::Features)
+
+QT_END_NAMESPACE
+
+#endif // QTEXTMARKDOWNIMPORTER_H
diff --git a/src/gui/text/qtextmarkdownwriter.cpp b/src/gui/text/qtextmarkdownwriter.cpp
new file mode 100644
index 0000000000..f351c8d20b
--- /dev/null
+++ b/src/gui/text/qtextmarkdownwriter.cpp
@@ -0,0 +1,559 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtextmarkdownwriter_p.h"
+#include "qtextdocumentlayout_p.h"
+#include "qfontinfo.h"
+#include "qfontmetrics.h"
+#include "qtextdocument_p.h"
+#include "qtextlist.h"
+#include "qtexttable.h"
+#include "qtextcursor.h"
+#include "qtextimagehandler_p.h"
+#include "qloggingcategory.h"
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcMDW, "qt.text.markdown.writer")
+
+static const QChar Space = QLatin1Char(' ');
+static const QChar Newline = QLatin1Char('\n');
+static const QChar LineBreak = QChar(0x2028);
+static const QChar DoubleQuote = QLatin1Char('"');
+static const QChar Backtick = QLatin1Char('`');
+static const QChar Period = QLatin1Char('.');
+
+QTextMarkdownWriter::QTextMarkdownWriter(QTextStream &stream, QTextDocument::MarkdownFeatures features)
+ : m_stream(stream), m_features(features)
+{
+}
+
+bool QTextMarkdownWriter::writeAll(const QTextDocument *document)
+{
+ writeFrame(document->rootFrame());
+ return true;
+}
+
+void QTextMarkdownWriter::writeTable(const QAbstractItemModel *table)
+{
+ QVector<int> tableColumnWidths(table->columnCount());
+ for (int col = 0; col < table->columnCount(); ++col) {
+ tableColumnWidths[col] = table->headerData(col, Qt::Horizontal).toString().length();
+ for (int row = 0; row < table->rowCount(); ++row) {
+ tableColumnWidths[col] = qMax(tableColumnWidths[col],
+ table->data(table->index(row, col)).toString().length());
+ }
+ }
+
+ // 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 << "|" << Qt::endl;
+ for (int col = 0; col < tableColumnWidths.length(); ++col)
+ m_stream << '|' << QString(tableColumnWidths[col], QLatin1Char('-'));
+ m_stream << '|'<< Qt::endl;
+
+ // write the body
+ 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 << '|'<< Qt::endl;
+ }
+ m_listInfo.clear();
+}
+
+void QTextMarkdownWriter::writeFrame(const QTextFrame *frame)
+{
+ Q_ASSERT(frame);
+ const QTextTable *table = qobject_cast<const QTextTable*> (frame);
+ QTextFrame::iterator iterator = frame->begin();
+ QTextFrame *child = nullptr;
+ int tableRow = -1;
+ bool lastWasList = false;
+ QVector<int> tableColumnWidths;
+ if (table) {
+ tableColumnWidths.resize(table->columns());
+ for (int col = 0; col < table->columns(); ++col) {
+ for (int row = 0; row < table->rows(); ++ row) {
+ QTextTableCell cell = table->cellAt(row, col);
+ int cellTextLen = 0;
+ auto it = cell.begin();
+ while (it != cell.end()) {
+ QTextBlock block = it.currentBlock();
+ if (block.isValid())
+ cellTextLen += block.text().length();
+ ++it;
+ }
+ if (cell.columnSpan() == 1 && tableColumnWidths[col] < cellTextLen)
+ tableColumnWidths[col] = cellTextLen;
+ }
+ }
+ }
+ while (!iterator.atEnd()) {
+ if (iterator.currentFrame() && child != iterator.currentFrame())
+ writeFrame(iterator.currentFrame());
+ else { // no frame, it's a block
+ QTextBlock block = iterator.currentBlock();
+ // Look ahead and detect some cases when we should
+ // suppress needless blank lines, when there will be a big change in block format
+ bool nextIsDifferent = false;
+ bool ending = false;
+ {
+ QTextFrame::iterator next = iterator;
+ ++next;
+ 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))
+ nextIsDifferent = true;
+ }
+ }
+ if (table) {
+ 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 << '|' << QString(tableColumnWidths[col], QLatin1Char('-'));
+ m_stream << '|';
+ }
+ m_stream << Newline << "|";
+ tableRow = cell.row();
+ }
+ } else if (!block.textList()) {
+ if (lastWasList)
+ m_stream << Newline;
+ }
+ int endingCol = writeBlock(block, !table, table && tableRow == 0, nextIsDifferent);
+ m_doubleNewlineWritten = false;
+ if (table) {
+ QTextTableCell cell = table->cellAt(block.position());
+ int paddingLen = -endingCol;
+ int spanEndCol = cell.column() + cell.columnSpan();
+ for (int col = cell.column(); col < spanEndCol; ++col)
+ paddingLen += tableColumnWidths[col];
+ if (paddingLen > 0)
+ m_stream << QString(paddingLen, Space);
+ for (int col = cell.column(); col < spanEndCol; ++col)
+ m_stream << "|";
+ } else if (m_fencedCodeBlock && ending) {
+ m_stream << m_linePrefix << QString(m_wrappedLineIndent, Space)
+ << m_codeBlockFence << Newline << Newline;
+ m_codeBlockFence.clear();
+ } else if (m_indentedCodeBlock && nextIsDifferent) {
+ m_stream << Newline;
+ } else if (endingCol > 0) {
+ if (block.textList() || block.blockFormat().hasProperty(QTextFormat::BlockCodeLanguage)) {
+ m_stream << Newline;
+ } else {
+ m_stream << Newline << Newline;
+ m_doubleNewlineWritten = true;
+ }
+ }
+ lastWasList = block.textList();
+ }
+ child = iterator.currentFrame();
+ ++iterator;
+ }
+ if (table) {
+ m_stream << Newline << Newline;
+ m_doubleNewlineWritten = true;
+ }
+ m_listInfo.clear();
+}
+
+QTextMarkdownWriter::ListInfo QTextMarkdownWriter::listInfo(QTextList *list)
+{
+ if (!m_listInfo.contains(list)) {
+ // decide whether this list is loose or tight
+ ListInfo info;
+ info.loose = false;
+ if (list->count() > 1) {
+ QTextBlock first = list->item(0);
+ QTextBlock last = list->item(list->count() - 1);
+ QTextBlock next = first.next();
+ while (next.isValid()) {
+ if (next == last)
+ break;
+ qCDebug(lcMDW) << "next block in list" << list << next.text() << "part of list?" << next.textList();
+ if (!next.textList()) {
+ // If we find a continuation paragraph, this list is "loose"
+ // because it will need a blank line to separate that paragraph.
+ qCDebug(lcMDW) << "decided list beginning with" << first.text() << "is loose after" << next.text();
+ info.loose = true;
+ break;
+ }
+ next = next.next();
+ }
+ }
+ m_listInfo.insert(list, info);
+ return info;
+ }
+ return m_listInfo.value(list);
+}
+
+static int nearestWordWrapIndex(const QString &s, int before)
+{
+ before = qMin(before, s.length());
+ 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) + QLatin1Char('<');
+ }
+ for (int i = before - 1; i >= 0; --i) {
+ if (s.at(i).isSpace()) {
+ qCDebug(lcMDW) << QString(i - fragBegin, Period) + QLatin1Char('^') << i;
+ return i;
+ }
+ }
+ qCDebug(lcMDW, "not possible");
+ return -1;
+}
+
+static int adjacentBackticksCount(const QString &s)
+{
+ int start = -1, len = s.length();
+ int ret = 0;
+ for (int i = 0; i < len; ++i) {
+ if (s.at(i) == Backtick) {
+ if (start < 0)
+ start = i;
+ } else if (start >= 0) {
+ ret = qMax(ret, i - start);
+ start = -1;
+ }
+ }
+ if (s.at(len - 1) == Backtick)
+ ret = qMax(ret, len - start);
+ return ret;
+}
+
+static void maybeEscapeFirstChar(QString &s)
+{
+ QString sTrimmed = s.trimmed();
+ if (sTrimmed.isEmpty())
+ return;
+ char firstChar = sTrimmed.at(0).toLatin1();
+ if (firstChar == '*' || firstChar == '+' || firstChar == '-') {
+ int i = s.indexOf(QLatin1Char(firstChar));
+ s.insert(i, QLatin1Char('\\'));
+ }
+}
+
+int QTextMarkdownWriter::writeBlock(const QTextBlock &block, bool wrap, bool ignoreFormat, bool ignoreEmpty)
+{
+ if (block.text().isEmpty() && ignoreEmpty)
+ return 0;
+ const int ColumnLimit = 80;
+ QTextBlockFormat blockFmt = block.blockFormat();
+ bool missedBlankCodeBlockLine = false;
+ 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;
+ QByteArray bullet = " ";
+ bool numeric = false;
+ switch (fmt.style()) {
+ case QTextListFormat::ListDisc:
+ bullet = "-";
+ m_wrappedLineIndent = 2;
+ break;
+ case QTextListFormat::ListCircle:
+ bullet = "*";
+ m_wrappedLineIndent = 2;
+ break;
+ case QTextListFormat::ListSquare:
+ bullet = "+";
+ m_wrappedLineIndent = 2;
+ break;
+ case QTextListFormat::ListStyleUndefined: break;
+ case QTextListFormat::ListDecimal:
+ case QTextListFormat::ListLowerAlpha:
+ case QTextListFormat::ListUpperAlpha:
+ case QTextListFormat::ListLowerRoman:
+ case QTextListFormat::ListUpperRoman:
+ numeric = true;
+ m_wrappedLineIndent = 4;
+ break;
+ }
+ switch (blockFmt.marker()) {
+ case QTextBlockFormat::Checked:
+ bullet += " [x]";
+ break;
+ case QTextBlockFormat::Unchecked:
+ bullet += " [ ]";
+ break;
+ default:
+ break;
+ }
+ int indentFirstLine = (listLevel - 1) * (numeric ? 4 : 2);
+ m_wrappedLineIndent += indentFirstLine;
+ if (m_lastListIndent != listLevel && !m_doubleNewlineWritten && listInfo(block.textList()).loose)
+ m_stream << Newline;
+ m_lastListIndent = listLevel;
+ QString prefix(indentFirstLine, 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;
+ prefix += numberStr;
+ } else {
+ prefix += QLatin1String(bullet) + Space;
+ }
+ m_stream << prefix;
+ } else if (blockFmt.hasProperty(QTextFormat::BlockTrailingHorizontalRulerWidth)) {
+ m_stream << "- - -\n"; // unambiguous horizontal rule, not an underline under a heading
+ return 0;
+ } else if (blockFmt.hasProperty(QTextFormat::BlockCodeFence) || blockFmt.stringProperty(QTextFormat::BlockCodeLanguage).length() > 0) {
+ // It's important to preserve blank lines in code blocks. But blank lines in code blocks
+ // inside block quotes are getting preserved anyway (along with the "> " prefix).
+ if (!blockFmt.hasProperty(QTextFormat::BlockQuoteLevel))
+ missedBlankCodeBlockLine = true; // only if we don't get any fragments below
+ if (!m_fencedCodeBlock) {
+ QString fenceChar = blockFmt.stringProperty(QTextFormat::BlockCodeFence);
+ if (fenceChar.isEmpty())
+ fenceChar = QLatin1String("`");
+ m_codeBlockFence = QString(3, fenceChar.at(0));
+ // A block quote can contain an indented code block, but not vice-versa.
+ m_stream << m_linePrefix << QString(m_wrappedLineIndent, Space) << m_codeBlockFence
+ << Space << blockFmt.stringProperty(QTextFormat::BlockCodeLanguage) << Newline;
+ m_fencedCodeBlock = true;
+ }
+ } else if (!blockFmt.indent()) {
+ if (m_fencedCodeBlock) {
+ m_stream << m_linePrefix << QString(m_wrappedLineIndent, Space)
+ << m_codeBlockFence << Newline;
+ m_fencedCodeBlock = false;
+ m_codeBlockFence.clear();
+ }
+ 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_indentedCodeBlock = true;
+ }
+ }
+ if (blockFmt.headingLevel())
+ m_stream << QByteArray(blockFmt.headingLevel(), '#') << ' ';
+ else
+ m_stream << m_linePrefix;
+
+ QString wrapIndentString = m_linePrefix + QString(m_wrappedLineIndent, 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();
+ bool mono = false;
+ bool startsOrEndsWithBacktick = false;
+ bool bold = false;
+ bool italic = false;
+ bool underline = false;
+ bool strikeOut = false;
+ QString backticks(Backtick);
+ for (QTextBlock::Iterator frag = block.begin(); !frag.atEnd(); ++frag) {
+ missedBlankCodeBlockLine = false;
+ QString fragmentText = frag.fragment().text();
+ while (fragmentText.endsWith(Newline))
+ fragmentText.chop(1);
+ if (block.textList()) { // <li>first line</br>continuation</li>
+ QString newlineIndent = QString(Newline) + QString(m_wrappedLineIndent, Space);
+ fragmentText.replace(QString(LineBreak), newlineIndent);
+ } else if (blockFmt.indent() > 0) { // <li>first line<p>continuation</p></li>
+ m_stream << QString(m_wrappedLineIndent, Space);
+ } else {
+ fragmentText.replace(LineBreak, Newline);
+ }
+ startsOrEndsWithBacktick |= fragmentText.startsWith(Backtick) || fragmentText.endsWith(Backtick);
+ QTextCharFormat fmt = frag.fragment().charFormat();
+ if (fmt.isImageFormat()) {
+ QTextImageFormat ifmt = fmt.toImageFormat();
+ QString desc = ifmt.stringProperty(QTextFormat::ImageAltText);
+ if (desc.isEmpty())
+ desc = QLatin1String("image");
+ QString s = QLatin1String("![") + desc + QLatin1String("](") + ifmt.name();
+ QString title = ifmt.stringProperty(QTextFormat::ImageTitle);
+ if (!title.isEmpty())
+ s += Space + DoubleQuote + title + DoubleQuote;
+ s += QLatin1Char(')');
+ if (wrap && col + s.length() > ColumnLimit) {
+ m_stream << Newline << wrapIndentString;
+ col = m_wrappedLineIndent;
+ }
+ m_stream << s;
+ col += s.length();
+ } else if (fmt.hasProperty(QTextFormat::AnchorHref)) {
+ QString s = QLatin1Char('[') + fragmentText + QLatin1String("](") +
+ fmt.property(QTextFormat::AnchorHref).toString() + QLatin1Char(')');
+ if (wrap && col + s.length() > ColumnLimit) {
+ m_stream << Newline << wrapIndentString;
+ col = m_wrappedLineIndent;
+ }
+ m_stream << s;
+ col += s.length();
+ } else {
+ QFontInfo fontInfo(fmt.font());
+ bool monoFrag = fontInfo.fixedPitch();
+ QString markers;
+ if (!ignoreFormat) {
+ if (monoFrag != mono && !m_indentedCodeBlock && !m_fencedCodeBlock) {
+ if (monoFrag)
+ backticks = QString(adjacentBackticksCount(fragmentText) + 1, Backtick);
+ markers += backticks;
+ if (startsOrEndsWithBacktick)
+ markers += Space;
+ mono = monoFrag;
+ }
+ if (!blockFmt.headingLevel() && !mono) {
+ if (fontInfo.bold() != bold) {
+ markers += QLatin1String("**");
+ bold = fontInfo.bold();
+ }
+ if (fontInfo.italic() != italic) {
+ markers += QLatin1Char('*');
+ italic = fontInfo.italic();
+ }
+ if (fontInfo.strikeOut() != strikeOut) {
+ markers += QLatin1String("~~");
+ strikeOut = fontInfo.strikeOut();
+ }
+ 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.
+ markers += QLatin1Char('_');
+ underline = fontInfo.underline();
+ }
+ }
+ }
+ if (wrap && col + markers.length() * 2 + fragmentText.length() > ColumnLimit) {
+ int i = 0;
+ int fragLen = fragmentText.length();
+ bool breakingLine = false;
+ while (i < fragLen) {
+ if (col >= ColumnLimit) {
+ m_stream << Newline << wrapIndentString;
+ col = m_wrappedLineIndent;
+ while (fragmentText[i].isSpace())
+ ++i;
+ }
+ int j = i + ColumnLimit - col;
+ if (j < fragLen) {
+ int wi = nearestWordWrapIndex(fragmentText, j);
+ if (wi < 0) {
+ j = fragLen;
+ } else if (wi >= i) {
+ j = wi;
+ breakingLine = true;
+ }
+ } else {
+ j = fragLen;
+ breakingLine = false;
+ }
+ QString subfrag = fragmentText.mid(i, j - i);
+ if (!i) {
+ m_stream << markers;
+ col += markers.length();
+ }
+ if (col == m_wrappedLineIndent)
+ maybeEscapeFirstChar(subfrag);
+ m_stream << subfrag;
+ if (breakingLine) {
+ m_stream << Newline << wrapIndentString;
+ col = m_wrappedLineIndent;
+ } else {
+ col += subfrag.length();
+ }
+ i = j + 1;
+ }
+ } else {
+ m_stream << markers << fragmentText;
+ col += markers.length() + fragmentText.length();
+ }
+ }
+ }
+ if (mono) {
+ if (startsOrEndsWithBacktick) {
+ m_stream << Space;
+ col += 1;
+ }
+ m_stream << backticks;
+ col += backticks.size();
+ }
+ if (bold) {
+ m_stream << "**";
+ col += 2;
+ }
+ if (italic) {
+ m_stream << "*";
+ col += 1;
+ }
+ if (underline) {
+ m_stream << "_";
+ col += 1;
+ }
+ if (strikeOut) {
+ m_stream << "~~";
+ col += 2;
+ }
+ if (missedBlankCodeBlockLine)
+ m_stream << Newline;
+ return col;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/text/qtextmarkdownwriter_p.h b/src/gui/text/qtextmarkdownwriter_p.h
new file mode 100644
index 0000000000..90310250ac
--- /dev/null
+++ b/src/gui/text/qtextmarkdownwriter_p.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTEXTMARKDOWNWRITER_P_H
+#define QTEXTMARKDOWNWRITER_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/QTextStream>
+
+#include "qtextdocument_p.h"
+#include "qtextdocumentwriter.h"
+#include "QAbstractTableModel"
+
+QT_BEGIN_NAMESPACE
+
+class Q_GUI_EXPORT QTextMarkdownWriter
+{
+public:
+ QTextMarkdownWriter(QTextStream &stream, QTextDocument::MarkdownFeatures features);
+ bool writeAll(const QTextDocument *document);
+ void writeTable(const QAbstractItemModel *table);
+
+ int writeBlock(const QTextBlock &block, bool table, bool ignoreFormat, bool ignoreEmpty);
+ void writeFrame(const QTextFrame *frame);
+
+private:
+ struct ListInfo {
+ bool loose;
+ };
+
+ ListInfo listInfo(QTextList *list);
+
+private:
+ QTextStream &m_stream;
+ QTextDocument::MarkdownFeatures m_features;
+ QMap<QTextList *, ListInfo> m_listInfo;
+ QString m_linePrefix;
+ QString m_codeBlockFence;
+ int m_wrappedLineIndent = 0;
+ int m_lastListIndent = 1;
+ bool m_doubleNewlineWritten = false;
+ bool m_indentedCodeBlock = false;
+ bool m_fencedCodeBlock = false;
+};
+
+QT_END_NAMESPACE
+
+#endif // QTEXTMARKDOWNWRITER_P_H
diff --git a/src/gui/text/qtextobject.cpp b/src/gui/text/qtextobject.cpp
index 547b21c02e..1302bd66bb 100644
--- a/src/gui/text/qtextobject.cpp
+++ b/src/gui/text/qtextobject.cpp
@@ -678,7 +678,7 @@ QTextFrame::iterator::iterator(QTextFrame *frame, int block, int begin, int end)
/*!
Copy constructor. Constructs a copy of the \a other iterator.
*/
-QTextFrame::iterator::iterator(const iterator &other) Q_DECL_NOTHROW
+QTextFrame::iterator::iterator(const iterator &other) noexcept
{
f = other.f;
b = other.b;
@@ -691,7 +691,7 @@ QTextFrame::iterator::iterator(const iterator &other) Q_DECL_NOTHROW
Assigns \a other to this iterator and returns a reference to
this iterator.
*/
-QTextFrame::iterator &QTextFrame::iterator::operator=(const iterator &other) Q_DECL_NOTHROW
+QTextFrame::iterator &QTextFrame::iterator::operator=(const iterator &other) noexcept
{
f = other.f;
b = other.b;
diff --git a/src/gui/text/qtextobject.h b/src/gui/text/qtextobject.h
index 694eb729d5..215a76fb4d 100644
--- a/src/gui/text/qtextobject.h
+++ b/src/gui/text/qtextobject.h
@@ -151,11 +151,11 @@ public:
public:
iterator(); // ### Qt 6: inline
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
- iterator(const iterator &o) Q_DECL_NOTHROW; // = default
- iterator &operator=(const iterator &o) Q_DECL_NOTHROW; // = default
- iterator(iterator &&other) Q_DECL_NOTHROW // = default
+ iterator(const iterator &o) noexcept; // = default
+ iterator &operator=(const iterator &o) noexcept; // = default
+ iterator(iterator &&other) noexcept // = default
{ memcpy(static_cast<void *>(this), static_cast<void *>(&other), sizeof(iterator)); }
- iterator &operator=(iterator &&other) Q_DECL_NOTHROW // = default
+ iterator &operator=(iterator &&other) noexcept // = default
{ memcpy(static_cast<void *>(this), static_cast<void *>(&other), sizeof(iterator)); return *this; }
#endif
diff --git a/src/gui/text/qtextobject_p.h b/src/gui/text/qtextobject_p.h
index 81ab023cc3..87c83868da 100644
--- a/src/gui/text/qtextobject_p.h
+++ b/src/gui/text/qtextobject_p.h
@@ -93,7 +93,7 @@ class QTextFramePrivate : public QTextObjectPrivate
Q_DECLARE_PUBLIC(QTextFrame)
public:
QTextFramePrivate(QTextDocument *doc)
- : QTextObjectPrivate(doc), fragment_start(0), fragment_end(0), parentFrame(0), layoutData(0)
+ : QTextObjectPrivate(doc), fragment_start(0), fragment_end(0), parentFrame(nullptr), layoutData(nullptr)
{
}
virtual void fragmentAdded(QChar type, uint fragment);
diff --git a/src/gui/text/qtextodfwriter.cpp b/src/gui/text/qtextodfwriter.cpp
index 9721243454..3561c185a6 100644
--- a/src/gui/text/qtextodfwriter.cpp
+++ b/src/gui/text/qtextodfwriter.cpp
@@ -523,9 +523,7 @@ void QTextOdfWriter::writeFormats(QXmlStreamWriter &writer, const QSet<int> &for
{
writer.writeStartElement(officeNS, QString::fromLatin1("automatic-styles"));
QVector<QTextFormat> allStyles = m_document->allFormats();
- QSetIterator<int> formatId(formats);
- while(formatId.hasNext()) {
- int formatIndex = formatId.next();
+ for (int formatIndex : formats) {
QTextFormat textFormat = allStyles.at(formatIndex);
switch (textFormat.type()) {
case QTextFormat::CharFormat:
@@ -1057,7 +1055,7 @@ bool QTextOdfWriter::writeAll()
// add objects for lists, frames and tables
const QVector<QTextFormat> allFormats = m_document->allFormats();
- const QList<int> copy = formats.toList();
+ const QList<int> copy = formats.values();
for (auto index : copy) {
QTextObject *object = m_document->objectForFormat(allFormats[index]);
if (object) {
diff --git a/src/gui/text/qtexttable_p.h b/src/gui/text/qtexttable_p.h
index c969e1d5bc..5c05611009 100644
--- a/src/gui/text/qtexttable_p.h
+++ b/src/gui/text/qtexttable_p.h
@@ -61,7 +61,7 @@ class QTextTablePrivate : public QTextFramePrivate
{
Q_DECLARE_PUBLIC(QTextTable)
public:
- QTextTablePrivate(QTextDocument *document) : QTextFramePrivate(document), grid(0), nRows(0), nCols(0), dirty(true), blockFragmentUpdates(false) {}
+ QTextTablePrivate(QTextDocument *document) : QTextFramePrivate(document), grid(nullptr), nRows(0), nCols(0), dirty(true), blockFragmentUpdates(false) {}
~QTextTablePrivate();
static QTextTable *createTable(QTextDocumentPrivate *, int pos, int rows, int cols, const QTextTableFormat &tableFormat);
diff --git a/src/gui/text/qzipreader_p.h b/src/gui/text/qzipreader_p.h
index 378072b366..6fec8d7719 100644
--- a/src/gui/text/qzipreader_p.h
+++ b/src/gui/text/qzipreader_p.h
@@ -79,11 +79,11 @@ public:
struct FileInfo
{
- FileInfo() Q_DECL_NOTHROW
+ FileInfo() noexcept
: isDir(false), isFile(false), isSymLink(false), crc(0), size(0)
{}
- bool isValid() const Q_DECL_NOTHROW { return isDir || isFile || isSymLink; }
+ bool isValid() const noexcept { return isDir || isFile || isSymLink; }
QString filePath;
uint isDir : 1;
diff --git a/src/gui/text/text.pri b/src/gui/text/text.pri
index abe20abe02..5e97b312f1 100644
--- a/src/gui/text/text.pri
+++ b/src/gui/text/text.pri
@@ -97,6 +97,25 @@ qtConfig(textodfwriter) {
text/qzip.cpp
}
+qtConfig(textmarkdownreader) {
+ qtConfig(system-textmarkdownreader) {
+ QMAKE_USE += libmd4c
+ } else {
+ include($$PWD/../../3rdparty/md4c.pri)
+ }
+ HEADERS += \
+ text/qtextmarkdownimporter_p.h
+ SOURCES += \
+ text/qtextmarkdownimporter.cpp
+}
+
+qtConfig(textmarkdownwriter) {
+ HEADERS += \
+ text/qtextmarkdownwriter_p.h
+ SOURCES += \
+ text/qtextmarkdownwriter.cpp
+}
+
qtConfig(cssparser) {
HEADERS += \
text/qcssparser_p.h
diff --git a/src/gui/util/qgridlayoutengine_p.h b/src/gui/util/qgridlayoutengine_p.h
index 5f0cc5da73..5f0e84edb1 100644
--- a/src/gui/util/qgridlayoutengine_p.h
+++ b/src/gui/util/qgridlayoutengine_p.h
@@ -180,7 +180,7 @@ public:
t = &q_minimumAscent;
break;
default:
- t = 0;
+ t = nullptr;
break;
}
return *t;
@@ -205,7 +205,7 @@ public:
t = &q_minimumAscent;
break;
default:
- t = 0;
+ t = nullptr;
break;
}
return *t;
@@ -276,7 +276,7 @@ class Q_GUI_EXPORT QGridLayoutItem
{
public:
QGridLayoutItem(int row, int column, int rowSpan = 1, int columnSpan = 1,
- Qt::Alignment alignment = 0);
+ Qt::Alignment alignment = nullptr);
virtual ~QGridLayoutItem() {}
inline int firstRow() const { return q_firstRows[Ver]; }
@@ -339,7 +339,7 @@ private:
class Q_GUI_EXPORT QGridLayoutEngine
{
public:
- QGridLayoutEngine(Qt::Alignment defaultAlignment = Qt::Alignment(0), bool snapToPixelGrid = false);
+ QGridLayoutEngine(Qt::Alignment defaultAlignment = Qt::Alignment(nullptr), bool snapToPixelGrid = false);
inline ~QGridLayoutEngine() { qDeleteAll(q_items); }
int rowCount(Qt::Orientation orientation) const;
diff --git a/src/gui/util/qshaderformat.cpp b/src/gui/util/qshaderformat.cpp
index e4e3718199..ea86dd6ca5 100644
--- a/src/gui/util/qshaderformat.cpp
+++ b/src/gui/util/qshaderformat.cpp
@@ -41,59 +41,59 @@
QT_BEGIN_NAMESPACE
-QShaderFormat::QShaderFormat() Q_DECL_NOTHROW
+QShaderFormat::QShaderFormat() noexcept
: m_api(NoApi)
, m_shaderType(Fragment)
{
}
-QShaderFormat::Api QShaderFormat::api() const Q_DECL_NOTHROW
+QShaderFormat::Api QShaderFormat::api() const noexcept
{
return m_api;
}
-void QShaderFormat::setApi(QShaderFormat::Api api) Q_DECL_NOTHROW
+void QShaderFormat::setApi(QShaderFormat::Api api) noexcept
{
m_api = api;
}
-QVersionNumber QShaderFormat::version() const Q_DECL_NOTHROW
+QVersionNumber QShaderFormat::version() const noexcept
{
return m_version;
}
-void QShaderFormat::setVersion(const QVersionNumber &version) Q_DECL_NOTHROW
+void QShaderFormat::setVersion(const QVersionNumber &version) noexcept
{
m_version = version;
}
-QStringList QShaderFormat::extensions() const Q_DECL_NOTHROW
+QStringList QShaderFormat::extensions() const noexcept
{
return m_extensions;
}
-void QShaderFormat::setExtensions(const QStringList &extensions) Q_DECL_NOTHROW
+void QShaderFormat::setExtensions(const QStringList &extensions) noexcept
{
m_extensions = extensions;
m_extensions.sort();
}
-QString QShaderFormat::vendor() const Q_DECL_NOTHROW
+QString QShaderFormat::vendor() const noexcept
{
return m_vendor;
}
-void QShaderFormat::setVendor(const QString &vendor) Q_DECL_NOTHROW
+void QShaderFormat::setVendor(const QString &vendor) noexcept
{
m_vendor = vendor;
}
-bool QShaderFormat::isValid() const Q_DECL_NOTHROW
+bool QShaderFormat::isValid() const noexcept
{
return m_api != NoApi && m_version.majorVersion() > 0;
}
-bool QShaderFormat::supports(const QShaderFormat &other) const Q_DECL_NOTHROW
+bool QShaderFormat::supports(const QShaderFormat &other) const noexcept
{
if (!isValid() || !other.isValid())
return false;
@@ -133,7 +133,7 @@ void QShaderFormat::setShaderType(QShaderFormat::ShaderType shaderType) Q_DECL_N
m_shaderType = shaderType;
}
-bool operator==(const QShaderFormat &lhs, const QShaderFormat &rhs) Q_DECL_NOTHROW
+bool operator==(const QShaderFormat &lhs, const QShaderFormat &rhs) noexcept
{
return lhs.api() == rhs.api()
&& lhs.version() == rhs.version()
diff --git a/src/gui/util/qshaderformat_p.h b/src/gui/util/qshaderformat_p.h
index 8d5e83bd22..c50d7d5729 100644
--- a/src/gui/util/qshaderformat_p.h
+++ b/src/gui/util/qshaderformat_p.h
@@ -78,22 +78,22 @@ public:
Compute
};
- Q_GUI_EXPORT QShaderFormat() Q_DECL_NOTHROW;
+ Q_GUI_EXPORT QShaderFormat() noexcept;
- Q_GUI_EXPORT Api api() const Q_DECL_NOTHROW;
- Q_GUI_EXPORT void setApi(Api api) Q_DECL_NOTHROW;
+ Q_GUI_EXPORT Api api() const noexcept;
+ Q_GUI_EXPORT void setApi(Api api) noexcept;
- Q_GUI_EXPORT QVersionNumber version() const Q_DECL_NOTHROW;
- Q_GUI_EXPORT void setVersion(const QVersionNumber &version) Q_DECL_NOTHROW;
+ Q_GUI_EXPORT QVersionNumber version() const noexcept;
+ Q_GUI_EXPORT void setVersion(const QVersionNumber &version) noexcept;
- Q_GUI_EXPORT QStringList extensions() const Q_DECL_NOTHROW;
- Q_GUI_EXPORT void setExtensions(const QStringList &extensions) Q_DECL_NOTHROW;
+ Q_GUI_EXPORT QStringList extensions() const noexcept;
+ Q_GUI_EXPORT void setExtensions(const QStringList &extensions) noexcept;
- Q_GUI_EXPORT QString vendor() const Q_DECL_NOTHROW;
- Q_GUI_EXPORT void setVendor(const QString &vendor) Q_DECL_NOTHROW;
+ Q_GUI_EXPORT QString vendor() const noexcept;
+ Q_GUI_EXPORT void setVendor(const QString &vendor) noexcept;
- Q_GUI_EXPORT bool isValid() const Q_DECL_NOTHROW;
- Q_GUI_EXPORT bool supports(const QShaderFormat &other) const Q_DECL_NOTHROW;
+ Q_GUI_EXPORT bool isValid() const noexcept;
+ Q_GUI_EXPORT bool supports(const QShaderFormat &other) const noexcept;
Q_GUI_EXPORT ShaderType shaderType() const Q_DECL_NOTHROW;
Q_GUI_EXPORT void setShaderType(ShaderType shaderType) Q_DECL_NOTHROW;
@@ -106,9 +106,9 @@ private:
ShaderType m_shaderType;
};
-Q_GUI_EXPORT bool operator==(const QShaderFormat &lhs, const QShaderFormat &rhs) Q_DECL_NOTHROW;
+Q_GUI_EXPORT bool operator==(const QShaderFormat &lhs, const QShaderFormat &rhs) noexcept;
-inline bool operator!=(const QShaderFormat &lhs, const QShaderFormat &rhs) Q_DECL_NOTHROW
+inline bool operator!=(const QShaderFormat &lhs, const QShaderFormat &rhs) noexcept
{
return !(lhs == rhs);
}
diff --git a/src/gui/util/qshadergraph.cpp b/src/gui/util/qshadergraph.cpp
index 40b85ac469..b05b710713 100644
--- a/src/gui/util/qshadergraph.cpp
+++ b/src/gui/util/qshadergraph.cpp
@@ -118,12 +118,12 @@ namespace
}
}
-QUuid QShaderGraph::Statement::uuid() const Q_DECL_NOTHROW
+QUuid QShaderGraph::Statement::uuid() const noexcept
{
return node.uuid();
}
-int QShaderGraph::Statement::portIndex(QShaderNodePort::Direction direction, const QString &portName) const Q_DECL_NOTHROW
+int QShaderGraph::Statement::portIndex(QShaderNodePort::Direction direction, const QString &portName) const noexcept
{
const QVector<QShaderNodePort> ports = node.ports();
int index = 0;
@@ -150,7 +150,7 @@ void QShaderGraph::removeNode(const QShaderNode &node)
m_nodes.erase(it);
}
-QVector<QShaderNode> QShaderGraph::nodes() const Q_DECL_NOTHROW
+QVector<QShaderNode> QShaderGraph::nodes() const noexcept
{
return m_nodes;
}
@@ -167,7 +167,7 @@ void QShaderGraph::removeEdge(const QShaderGraph::Edge &edge)
m_edges.removeAll(edge);
}
-QVector<QShaderGraph::Edge> QShaderGraph::edges() const Q_DECL_NOTHROW
+QVector<QShaderGraph::Edge> QShaderGraph::edges() const noexcept
{
return m_edges;
}
@@ -244,7 +244,7 @@ QVector<QShaderGraph::Statement> QShaderGraph::createStatements(const QStringLis
return result;
}
-bool operator==(const QShaderGraph::Edge &lhs, const QShaderGraph::Edge &rhs) Q_DECL_NOTHROW
+bool operator==(const QShaderGraph::Edge &lhs, const QShaderGraph::Edge &rhs) noexcept
{
return lhs.sourceNodeUuid == rhs.sourceNodeUuid
&& lhs.sourcePortName == rhs.sourcePortName
@@ -252,7 +252,7 @@ bool operator==(const QShaderGraph::Edge &lhs, const QShaderGraph::Edge &rhs) Q_
&& lhs.targetPortName == rhs.targetPortName;
}
-bool operator==(const QShaderGraph::Statement &lhs, const QShaderGraph::Statement &rhs) Q_DECL_NOTHROW
+bool operator==(const QShaderGraph::Statement &lhs, const QShaderGraph::Statement &rhs) noexcept
{
return lhs.inputs == rhs.inputs
&& lhs.outputs == rhs.outputs
diff --git a/src/gui/util/qshadergraph_p.h b/src/gui/util/qshadergraph_p.h
index 756e1b2da2..9367a3783c 100644
--- a/src/gui/util/qshadergraph_p.h
+++ b/src/gui/util/qshadergraph_p.h
@@ -73,8 +73,8 @@ public:
class Statement
{
public:
- Q_GUI_EXPORT QUuid uuid() const Q_DECL_NOTHROW;
- Q_GUI_EXPORT int portIndex(QShaderNodePort::Direction direction, const QString &portName) const Q_DECL_NOTHROW;
+ Q_GUI_EXPORT QUuid uuid() const noexcept;
+ Q_GUI_EXPORT int portIndex(QShaderNodePort::Direction direction, const QString &portName) const noexcept;
QShaderNode node;
QVector<int> inputs;
@@ -83,11 +83,11 @@ public:
Q_GUI_EXPORT void addNode(const QShaderNode &node);
Q_GUI_EXPORT void removeNode(const QShaderNode &node);
- Q_GUI_EXPORT QVector<QShaderNode> nodes() const Q_DECL_NOTHROW;
+ Q_GUI_EXPORT QVector<QShaderNode> nodes() const noexcept;
Q_GUI_EXPORT void addEdge(const Edge &edge);
Q_GUI_EXPORT void removeEdge(const Edge &edge);
- Q_GUI_EXPORT QVector<Edge> edges() const Q_DECL_NOTHROW;
+ Q_GUI_EXPORT QVector<Edge> edges() const noexcept;
Q_GUI_EXPORT QVector<Statement> createStatements(const QStringList &enabledLayers = QStringList()) const;
@@ -96,16 +96,16 @@ private:
QVector<Edge> m_edges;
};
-Q_GUI_EXPORT bool operator==(const QShaderGraph::Edge &lhs, const QShaderGraph::Edge &rhs) Q_DECL_NOTHROW;
+Q_GUI_EXPORT bool operator==(const QShaderGraph::Edge &lhs, const QShaderGraph::Edge &rhs) noexcept;
-inline bool operator!=(const QShaderGraph::Edge &lhs, const QShaderGraph::Edge &rhs) Q_DECL_NOTHROW
+inline bool operator!=(const QShaderGraph::Edge &lhs, const QShaderGraph::Edge &rhs) noexcept
{
return !(lhs == rhs);
}
-Q_GUI_EXPORT bool operator==(const QShaderGraph::Statement &lhs, const QShaderGraph::Statement &rhs) Q_DECL_NOTHROW;
+Q_GUI_EXPORT bool operator==(const QShaderGraph::Statement &lhs, const QShaderGraph::Statement &rhs) noexcept;
-inline bool operator!=(const QShaderGraph::Statement &lhs, const QShaderGraph::Statement &rhs) Q_DECL_NOTHROW
+inline bool operator!=(const QShaderGraph::Statement &lhs, const QShaderGraph::Statement &rhs) noexcept
{
return !(lhs == rhs);
}
diff --git a/src/gui/util/qshadergraphloader.cpp b/src/gui/util/qshadergraphloader.cpp
index b9d8318655..a393e876e0 100644
--- a/src/gui/util/qshadergraphloader.cpp
+++ b/src/gui/util/qshadergraphloader.cpp
@@ -52,29 +52,29 @@ QT_BEGIN_NAMESPACE
void qt_register_ShaderLanguage_enums();
-QShaderGraphLoader::QShaderGraphLoader() Q_DECL_NOTHROW
+QShaderGraphLoader::QShaderGraphLoader() noexcept
: m_status(Null),
m_device(nullptr)
{
qt_register_ShaderLanguage_enums();
}
-QShaderGraphLoader::Status QShaderGraphLoader::status() const Q_DECL_NOTHROW
+QShaderGraphLoader::Status QShaderGraphLoader::status() const noexcept
{
return m_status;
}
-QShaderGraph QShaderGraphLoader::graph() const Q_DECL_NOTHROW
+QShaderGraph QShaderGraphLoader::graph() const noexcept
{
return m_graph;
}
-QIODevice *QShaderGraphLoader::device() const Q_DECL_NOTHROW
+QIODevice *QShaderGraphLoader::device() const noexcept
{
return m_device;
}
-void QShaderGraphLoader::setDevice(QIODevice *device) Q_DECL_NOTHROW
+void QShaderGraphLoader::setDevice(QIODevice *device) noexcept
{
m_device = device;
m_graph = QShaderGraph();
@@ -83,12 +83,12 @@ void QShaderGraphLoader::setDevice(QIODevice *device) Q_DECL_NOTHROW
: Error;
}
-QHash<QString, QShaderNode> QShaderGraphLoader::prototypes() const Q_DECL_NOTHROW
+QHash<QString, QShaderNode> QShaderGraphLoader::prototypes() const noexcept
{
return m_prototypes;
}
-void QShaderGraphLoader::setPrototypes(const QHash<QString, QShaderNode> &prototypes) Q_DECL_NOTHROW
+void QShaderGraphLoader::setPrototypes(const QHash<QString, QShaderNode> &prototypes) noexcept
{
m_prototypes = prototypes;
}
diff --git a/src/gui/util/qshadergraphloader_p.h b/src/gui/util/qshadergraphloader_p.h
index 97cbd8d18c..e7aa19fa2d 100644
--- a/src/gui/util/qshadergraphloader_p.h
+++ b/src/gui/util/qshadergraphloader_p.h
@@ -69,16 +69,16 @@ public:
Error
};
- Q_GUI_EXPORT QShaderGraphLoader() Q_DECL_NOTHROW;
+ Q_GUI_EXPORT QShaderGraphLoader() noexcept;
- Q_GUI_EXPORT Status status() const Q_DECL_NOTHROW;
- Q_GUI_EXPORT QShaderGraph graph() const Q_DECL_NOTHROW;
+ Q_GUI_EXPORT Status status() const noexcept;
+ Q_GUI_EXPORT QShaderGraph graph() const noexcept;
- Q_GUI_EXPORT QIODevice *device() const Q_DECL_NOTHROW;
- Q_GUI_EXPORT void setDevice(QIODevice *device) Q_DECL_NOTHROW;
+ Q_GUI_EXPORT QIODevice *device() const noexcept;
+ Q_GUI_EXPORT void setDevice(QIODevice *device) noexcept;
- Q_GUI_EXPORT QHash<QString, QShaderNode> prototypes() const Q_DECL_NOTHROW;
- Q_GUI_EXPORT void setPrototypes(const QHash<QString, QShaderNode> &prototypes) Q_DECL_NOTHROW;
+ Q_GUI_EXPORT QHash<QString, QShaderNode> prototypes() const noexcept;
+ Q_GUI_EXPORT void setPrototypes(const QHash<QString, QShaderNode> &prototypes) noexcept;
Q_GUI_EXPORT void load();
diff --git a/src/gui/util/qshadernode.cpp b/src/gui/util/qshadernode.cpp
index e570880d1d..547e5c51a8 100644
--- a/src/gui/util/qshadernode.cpp
+++ b/src/gui/util/qshadernode.cpp
@@ -41,7 +41,7 @@
QT_BEGIN_NAMESPACE
-QShaderNode::Type QShaderNode::type() const Q_DECL_NOTHROW
+QShaderNode::Type QShaderNode::type() const noexcept
{
int inputCount = 0;
int outputCount = 0;
@@ -62,27 +62,27 @@ QShaderNode::Type QShaderNode::type() const Q_DECL_NOTHROW
: Function;
}
-QUuid QShaderNode::uuid() const Q_DECL_NOTHROW
+QUuid QShaderNode::uuid() const noexcept
{
return m_uuid;
}
-void QShaderNode::setUuid(const QUuid &uuid) Q_DECL_NOTHROW
+void QShaderNode::setUuid(const QUuid &uuid) noexcept
{
m_uuid = uuid;
}
-QStringList QShaderNode::layers() const Q_DECL_NOTHROW
+QStringList QShaderNode::layers() const noexcept
{
return m_layers;
}
-void QShaderNode::setLayers(const QStringList &layers) Q_DECL_NOTHROW
+void QShaderNode::setLayers(const QStringList &layers) noexcept
{
m_layers = layers;
}
-QVector<QShaderNodePort> QShaderNode::ports() const Q_DECL_NOTHROW
+QVector<QShaderNodePort> QShaderNode::ports() const noexcept
{
return m_ports;
}
@@ -157,13 +157,13 @@ QShaderNode::Rule QShaderNode::rule(const QShaderFormat &format) const
return it != m_rules.crend() ? it->second : Rule();
}
-QShaderNode::Rule::Rule(const QByteArray &subs, const QByteArrayList &snippets) Q_DECL_NOTHROW
+QShaderNode::Rule::Rule(const QByteArray &subs, const QByteArrayList &snippets) noexcept
: substitution(subs),
headerSnippets(snippets)
{
}
-bool operator==(const QShaderNode::Rule &lhs, const QShaderNode::Rule &rhs) Q_DECL_NOTHROW
+bool operator==(const QShaderNode::Rule &lhs, const QShaderNode::Rule &rhs) noexcept
{
return lhs.substitution == rhs.substitution
&& lhs.headerSnippets == rhs.headerSnippets;
diff --git a/src/gui/util/qshadernode_p.h b/src/gui/util/qshadernode_p.h
index 12ff9b2a2c..c3d149d8a5 100644
--- a/src/gui/util/qshadernode_p.h
+++ b/src/gui/util/qshadernode_p.h
@@ -73,21 +73,21 @@ public:
class Rule
{
public:
- Q_GUI_EXPORT Rule(const QByteArray &substitution = QByteArray(), const QByteArrayList &headerSnippets = QByteArrayList()) Q_DECL_NOTHROW;
+ Q_GUI_EXPORT Rule(const QByteArray &substitution = QByteArray(), const QByteArrayList &headerSnippets = QByteArrayList()) noexcept;
QByteArray substitution;
QByteArrayList headerSnippets;
};
- Q_GUI_EXPORT Type type() const Q_DECL_NOTHROW;
+ Q_GUI_EXPORT Type type() const noexcept;
- Q_GUI_EXPORT QUuid uuid() const Q_DECL_NOTHROW;
- Q_GUI_EXPORT void setUuid(const QUuid &uuid) Q_DECL_NOTHROW;
+ Q_GUI_EXPORT QUuid uuid() const noexcept;
+ Q_GUI_EXPORT void setUuid(const QUuid &uuid) noexcept;
- Q_GUI_EXPORT QStringList layers() const Q_DECL_NOTHROW;
- Q_GUI_EXPORT void setLayers(const QStringList &layers) Q_DECL_NOTHROW;
+ Q_GUI_EXPORT QStringList layers() const noexcept;
+ Q_GUI_EXPORT void setLayers(const QStringList &layers) noexcept;
- Q_GUI_EXPORT QVector<QShaderNodePort> ports() const Q_DECL_NOTHROW;
+ Q_GUI_EXPORT QVector<QShaderNodePort> ports() const noexcept;
Q_GUI_EXPORT void addPort(const QShaderNodePort &port);
Q_GUI_EXPORT void removePort(const QShaderNodePort &port);
@@ -110,9 +110,9 @@ private:
QVector<QPair<QShaderFormat, QShaderNode::Rule>> m_rules;
};
-Q_GUI_EXPORT bool operator==(const QShaderNode::Rule &lhs, const QShaderNode::Rule &rhs) Q_DECL_NOTHROW;
+Q_GUI_EXPORT bool operator==(const QShaderNode::Rule &lhs, const QShaderNode::Rule &rhs) noexcept;
-inline bool operator!=(const QShaderNode::Rule &lhs, const QShaderNode::Rule &rhs) Q_DECL_NOTHROW
+inline bool operator!=(const QShaderNode::Rule &lhs, const QShaderNode::Rule &rhs) noexcept
{
return !(lhs == rhs);
}
diff --git a/src/gui/util/qshadernodeport.cpp b/src/gui/util/qshadernodeport.cpp
index 03646a9467..15dbc4160e 100644
--- a/src/gui/util/qshadernodeport.cpp
+++ b/src/gui/util/qshadernodeport.cpp
@@ -41,12 +41,12 @@
QT_BEGIN_NAMESPACE
-QShaderNodePort::QShaderNodePort() Q_DECL_NOTHROW
+QShaderNodePort::QShaderNodePort() noexcept
: direction(Output)
{
}
-bool operator==(const QShaderNodePort &lhs, const QShaderNodePort &rhs) Q_DECL_NOTHROW
+bool operator==(const QShaderNodePort &lhs, const QShaderNodePort &rhs) noexcept
{
return lhs.direction == rhs.direction
&& lhs.name == rhs.name;
diff --git a/src/gui/util/qshadernodeport_p.h b/src/gui/util/qshadernodeport_p.h
index cfdaf05017..c69ba23a3f 100644
--- a/src/gui/util/qshadernodeport_p.h
+++ b/src/gui/util/qshadernodeport_p.h
@@ -66,15 +66,15 @@ public:
Output
};
- Q_GUI_EXPORT QShaderNodePort() Q_DECL_NOTHROW;
+ Q_GUI_EXPORT QShaderNodePort() noexcept;
QShaderNodePort::Direction direction;
QString name;
};
-Q_GUI_EXPORT bool operator==(const QShaderNodePort &lhs, const QShaderNodePort &rhs) Q_DECL_NOTHROW;
+Q_GUI_EXPORT bool operator==(const QShaderNodePort &lhs, const QShaderNodePort &rhs) noexcept;
-inline bool operator!=(const QShaderNodePort &lhs, const QShaderNodePort &rhs) Q_DECL_NOTHROW
+inline bool operator!=(const QShaderNodePort &lhs, const QShaderNodePort &rhs) noexcept
{
return !(lhs == rhs);
}
diff --git a/src/gui/util/qshadernodesloader.cpp b/src/gui/util/qshadernodesloader.cpp
index 5369e8bd4c..922479332c 100644
--- a/src/gui/util/qshadernodesloader.cpp
+++ b/src/gui/util/qshadernodesloader.cpp
@@ -48,28 +48,28 @@
QT_BEGIN_NAMESPACE
-QShaderNodesLoader::QShaderNodesLoader() Q_DECL_NOTHROW
+QShaderNodesLoader::QShaderNodesLoader() noexcept
: m_status(Null),
m_device(nullptr)
{
}
-QShaderNodesLoader::Status QShaderNodesLoader::status() const Q_DECL_NOTHROW
+QShaderNodesLoader::Status QShaderNodesLoader::status() const noexcept
{
return m_status;
}
-QHash<QString, QShaderNode> QShaderNodesLoader::nodes() const Q_DECL_NOTHROW
+QHash<QString, QShaderNode> QShaderNodesLoader::nodes() const noexcept
{
return m_nodes;
}
-QIODevice *QShaderNodesLoader::device() const Q_DECL_NOTHROW
+QIODevice *QShaderNodesLoader::device() const noexcept
{
return m_device;
}
-void QShaderNodesLoader::setDevice(QIODevice *device) Q_DECL_NOTHROW
+void QShaderNodesLoader::setDevice(QIODevice *device) noexcept
{
m_device = device;
m_nodes.clear();
diff --git a/src/gui/util/qshadernodesloader_p.h b/src/gui/util/qshadernodesloader_p.h
index 0bec871857..432d7d4494 100644
--- a/src/gui/util/qshadernodesloader_p.h
+++ b/src/gui/util/qshadernodesloader_p.h
@@ -69,13 +69,13 @@ public:
Error
};
- Q_GUI_EXPORT QShaderNodesLoader() Q_DECL_NOTHROW;
+ Q_GUI_EXPORT QShaderNodesLoader() noexcept;
- Q_GUI_EXPORT Status status() const Q_DECL_NOTHROW;
- Q_GUI_EXPORT QHash<QString, QShaderNode> nodes() const Q_DECL_NOTHROW;
+ Q_GUI_EXPORT Status status() const noexcept;
+ Q_GUI_EXPORT QHash<QString, QShaderNode> nodes() const noexcept;
- Q_GUI_EXPORT QIODevice *device() const Q_DECL_NOTHROW;
- Q_GUI_EXPORT void setDevice(QIODevice *device) Q_DECL_NOTHROW;
+ Q_GUI_EXPORT QIODevice *device() const noexcept;
+ Q_GUI_EXPORT void setDevice(QIODevice *device) noexcept;
Q_GUI_EXPORT void load();
Q_GUI_EXPORT void load(const QJsonObject &prototypesObject);
diff --git a/src/gui/util/qvalidator.h b/src/gui/util/qvalidator.h
index cc7cbcb559..f0e72e3814 100644
--- a/src/gui/util/qvalidator.h
+++ b/src/gui/util/qvalidator.h
@@ -69,6 +69,7 @@ public:
Intermediate,
Acceptable
};
+ Q_ENUM(State)
void setLocale(const QLocale &locale);
QLocale locale() const;
diff --git a/src/gui/util/util.pri b/src/gui/util/util.pri
index e5e711b1a0..d3402133d6 100644
--- a/src/gui/util/util.pri
+++ b/src/gui/util/util.pri
@@ -8,7 +8,6 @@ HEADERS += \
util/qabstractlayoutstyleinfo_p.h \
util/qlayoutpolicy_p.h \
util/qshaderformat_p.h \
- util/qshadergenerator_p.h \
util/qshadergraph_p.h \
util/qshadergraphloader_p.h \
util/qshaderlanguage_p.h \
@@ -29,7 +28,6 @@ SOURCES += \
util/qabstractlayoutstyleinfo.cpp \
util/qlayoutpolicy.cpp \
util/qshaderformat.cpp \
- util/qshadergenerator.cpp \
util/qshadergraph.cpp \
util/qshadergraphloader.cpp \
util/qshaderlanguage.cpp \
@@ -41,3 +39,10 @@ SOURCES += \
util/qpkmhandler.cpp \
util/qktxhandler.cpp \
util/qastchandler.cpp
+
+qtConfig(regularexpression) {
+ HEADERS += \
+ util/qshadergenerator_p.h
+ SOURCES += \
+ util/qshadergenerator.cpp
+}
diff --git a/src/gui/vulkan/qplatformvulkaninstance.cpp b/src/gui/vulkan/qplatformvulkaninstance.cpp
index 6201d3ec11..9d044bfd58 100644
--- a/src/gui/vulkan/qplatformvulkaninstance.cpp
+++ b/src/gui/vulkan/qplatformvulkaninstance.cpp
@@ -85,4 +85,9 @@ void QPlatformVulkanInstance::presentQueued(QWindow *window)
Q_UNUSED(window);
}
+void QPlatformVulkanInstance::setDebugFilters(const QVector<QVulkanInstance::DebugFilter> &filters)
+{
+ Q_UNUSED(filters);
+}
+
QT_END_NAMESPACE
diff --git a/src/gui/vulkan/qplatformvulkaninstance.h b/src/gui/vulkan/qplatformvulkaninstance.h
index 9f34803f7b..d47c59b5db 100644
--- a/src/gui/vulkan/qplatformvulkaninstance.h
+++ b/src/gui/vulkan/qplatformvulkaninstance.h
@@ -78,6 +78,7 @@ public:
virtual PFN_vkVoidFunction getInstanceProcAddr(const char *name) = 0;
virtual bool supportsPresent(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, QWindow *window) = 0;
virtual void presentQueued(QWindow *window);
+ virtual void setDebugFilters(const QVector<QVulkanInstance::DebugFilter> &filters);
private:
QScopedPointer<QPlatformVulkanInstancePrivate> d_ptr;
diff --git a/src/gui/vulkan/qvulkaninstance.cpp b/src/gui/vulkan/qvulkaninstance.cpp
index 2941bfd01f..0605d88cca 100644
--- a/src/gui/vulkan/qvulkaninstance.cpp
+++ b/src/gui/vulkan/qvulkaninstance.cpp
@@ -269,6 +269,7 @@ public:
VkResult errorCode;
QScopedPointer<QVulkanFunctions> funcs;
QHash<VkDevice, QVulkanDeviceFunctions *> deviceFuncs;
+ QVector<QVulkanInstance::DebugFilter> debugFilters;
};
bool QVulkanInstancePrivate::ensureVulkan()
@@ -570,6 +571,7 @@ bool QVulkanInstance::create()
d_ptr->extensions = d_ptr->platformInst->enabledExtensions();
d_ptr->errorCode = VK_SUCCESS;
d_ptr->funcs.reset(new QVulkanFunctions(this));
+ d_ptr->platformInst->setDebugFilters(d_ptr->debugFilters);
return true;
}
@@ -785,6 +787,50 @@ void QVulkanInstance::presentQueued(QWindow *window)
d_ptr->platformInst->presentQueued(window);
}
+/*!
+ \typedef QVulkanInstance::DebugFilter
+
+ Typedef for debug filtering callback functions.
+
+ \sa installDebugOutputFilter(), removeDebugOutputFilter()
+ */
+
+/*!
+ 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 removeDebugOutputFilter()
+ */
+void QVulkanInstance::installDebugOutputFilter(DebugFilter filter)
+{
+ if (!d_ptr->debugFilters.contains(filter)) {
+ d_ptr->debugFilters.append(filter);
+ if (d_ptr->platformInst)
+ d_ptr->platformInst->setDebugFilters(d_ptr->debugFilters);
+ }
+}
+
+/*!
+ Removes a \a filter function previously installed by
+ installDebugOutputFilter().
+
+ \note This function can be called before create().
+
+ \sa installDebugOutputFilter()
+ */
+void QVulkanInstance::removeDebugOutputFilter(DebugFilter filter)
+{
+ d_ptr->debugFilters.removeOne(filter);
+ if (d_ptr->platformInst)
+ d_ptr->platformInst->setDebugFilters(d_ptr->debugFilters);
+}
+
#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 57459e458c..70f2fd5102 100644
--- a/src/gui/vulkan/qvulkaninstance.h
+++ b/src/gui/vulkan/qvulkaninstance.h
@@ -84,14 +84,14 @@ struct QVulkanLayer
};
Q_DECLARE_TYPEINFO(QVulkanLayer, Q_MOVABLE_TYPE);
-inline bool operator==(const QVulkanLayer &lhs, const QVulkanLayer &rhs) Q_DECL_NOTHROW
+inline bool operator==(const QVulkanLayer &lhs, const QVulkanLayer &rhs) noexcept
{
return lhs.name == rhs.name && lhs.version == rhs.version && lhs.specVersion == rhs.specVersion;
}
-inline bool operator!=(const QVulkanLayer &lhs, const QVulkanLayer &rhs) Q_DECL_NOTHROW
+inline bool operator!=(const QVulkanLayer &lhs, const QVulkanLayer &rhs) noexcept
{ return !(lhs == rhs); }
-inline uint qHash(const QVulkanLayer &key, uint seed = 0) Q_DECL_NOTHROW
+inline uint qHash(const QVulkanLayer &key, uint seed = 0) noexcept
{
QtPrivate::QHashCombine hash;
seed = hash(seed, key.name);
@@ -107,14 +107,14 @@ struct QVulkanExtension
};
Q_DECLARE_TYPEINFO(QVulkanExtension, Q_MOVABLE_TYPE);
-inline bool operator==(const QVulkanExtension &lhs, const QVulkanExtension &rhs) Q_DECL_NOTHROW
+inline bool operator==(const QVulkanExtension &lhs, const QVulkanExtension &rhs) noexcept
{
return lhs.name == rhs.name && lhs.version == rhs.version;
}
-inline bool operator!=(const QVulkanExtension &lhs, const QVulkanExtension &rhs) Q_DECL_NOTHROW
+inline bool operator!=(const QVulkanExtension &lhs, const QVulkanExtension &rhs) noexcept
{ return !(lhs == rhs); }
-inline uint qHash(const QVulkanExtension &key, uint seed = 0) Q_DECL_NOTHROW
+inline uint qHash(const QVulkanExtension &key, uint seed = 0) noexcept
{
QtPrivate::QHashCombine hash;
seed = hash(seed, key.name);
@@ -188,6 +188,11 @@ public:
void presentQueued(QWindow *window);
+ typedef bool (*DebugFilter)(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object,
+ size_t location, int32_t messageCode, const char *pLayerPrefix, const char *pMessage);
+ void installDebugOutputFilter(DebugFilter filter);
+ void removeDebugOutputFilter(DebugFilter filter);
+
private:
QScopedPointer<QVulkanInstancePrivate> d_ptr;
Q_DISABLE_COPY(QVulkanInstance)
diff --git a/src/gui/vulkan/qvulkanwindow_p.h b/src/gui/vulkan/qvulkanwindow_p.h
index c6a772bc31..fb374a5564 100644
--- a/src/gui/vulkan/qvulkanwindow_p.h
+++ b/src/gui/vulkan/qvulkanwindow_p.h
@@ -97,7 +97,7 @@ public:
int physDevIndex = 0;
QVector<VkPhysicalDevice> physDevs;
QVector<VkPhysicalDeviceProperties> physDevProps;
- QVulkanWindow::Flags flags = 0;
+ QVulkanWindow::Flags flags = nullptr;
QByteArrayList requestedDevExtensions;
QHash<VkPhysicalDevice, QVulkanInfoVector<QVulkanExtension> > supportedDevExtensions;
QVector<VkFormat> requestedColorFormats;