summaryrefslogtreecommitdiffstats
path: root/src/tools
diff options
context:
space:
mode:
authorLaszlo Agocs <laszlo.agocs@qt.io>2021-01-14 16:40:14 +0100
committerLaszlo Agocs <laszlo.agocs@qt.io>2021-01-19 14:17:55 +0100
commitc6d602990901bb44b9268b1603b25d3c76c97683 (patch)
tree23a5cc95fa7e3bb9f0a8b22dd722da1a252b2c2b /src/tools
parent12d8bb0709bf7982061cb0c3e608e4a581892e35 (diff)
Update QVulkan(Device)Functions to Vulkan 1.2
This also needs improvements to qvkgen. What we get with this patch are the Vulkan 1.1 and 1.2 core API's additional 11 instance-level and 30 device-level commands present in QVulkanFunctions and QVulkanDeviceFunctions. All of these are attempted to be resolved upon construction. When the implementation does not return a valid function pointer for some of them (e.g. because it is a Vulkan 1.0 instance or physical device), calling the corresponding wrapper functions will lead to unspecified behavior. This is in line with how QOpenGLExtraFunctions works. The simple autotest added to exercise some Vulkan 1.1 APIs demonstrates this in action. The member functions in the generated qvulkan(device)functions header and source files are ifdefed by VK_VERSION_1_{0,1,2}. This is essential because otherwise a Qt build made on a system with Vulkan 1.2 headers would cause compilation breaks in application build environments with Vulkan 1.0/1.1 headers when including qvulkanfunctions.h (due to missing the 1.1/1.2 types and constants, some of which are used in the function prototypes). In practice this should be alright - the only caveat to keep in mind is that the Qt builds meant to be distributed to a wide variety of systems need to be made with a sufficiently new version of the Vulkan headers installed, just to ensure that the 1.1 and 1.2 wrapper functions are compiled into the Qt libraries. Task-number: QTBUG-90219 Change-Id: I48360a8a2e915d2709fe82993f65e99b2ccd5d53 Reviewed-by: Andy Nichols <andy.nichols@qt.io>
Diffstat (limited to 'src/tools')
-rw-r--r--src/tools/qvkgen/qvkgen.cpp201
1 files changed, 159 insertions, 42 deletions
diff --git a/src/tools/qvkgen/qvkgen.cpp b/src/tools/qvkgen/qvkgen.cpp
index 8135678fe1..798cbb7932 100644
--- a/src/tools/qvkgen/qvkgen.cpp
+++ b/src/tools/qvkgen/qvkgen.cpp
@@ -32,6 +32,13 @@
#include <QtCore/qlist.h>
#include <QtCore/qxmlstream.h>
+// generate wrappers for core functions from the following versions
+static const QStringList VERSIONS = {
+ QStringLiteral("VK_VERSION_1_0"), // must be the first and always present
+ QStringLiteral("VK_VERSION_1_1"),
+ QStringLiteral("VK_VERSION_1_2")
+};
+
class VkSpecParser
{
public:
@@ -50,11 +57,14 @@ public:
};
QList<Command> commands() const { return m_commands; }
+ QMap<QString, QStringList> versionCommandMapping() const { return m_versionCommandMapping; }
void setFileName(const QString &fn) { m_fn = fn; }
private:
void skip();
+ void parseFeature();
+ void parseFeatureRequire(const QString &versionDefine);
void parseCommands();
Command parseCommand();
TypedName parseParamOrProto(const QString &tag);
@@ -63,6 +73,7 @@ private:
QFile m_file;
QXmlStreamReader m_reader;
QList<Command> m_commands;
+ QMap<QString, QStringList> m_versionCommandMapping; // "1.0" -> ["vkGetPhysicalDeviceProperties", ...]
QString m_fn;
};
@@ -73,13 +84,18 @@ bool VkSpecParser::parse()
qWarning("Failed to open %s", qPrintable(m_file.fileName()));
return false;
}
-
m_reader.setDevice(&m_file);
+
+ m_commands.clear();
+ m_versionCommandMapping.clear();
+
while (!m_reader.atEnd()) {
m_reader.readNext();
if (m_reader.isStartElement()) {
if (m_reader.name() == QStringLiteral("commands"))
parseCommands();
+ else if (m_reader.name() == QStringLiteral("feature"))
+ parseFeature();
}
}
@@ -96,15 +112,60 @@ void VkSpecParser::skip()
}
}
+void VkSpecParser::parseFeature()
+{
+ // <feature api="vulkan" name="VK_VERSION_1_0" number="1.0" comment="Vulkan core API interface definitions">
+ // <require comment="Device initialization">
+
+ QString api;
+ QString versionName;
+ for (const QXmlStreamAttribute &attr : m_reader.attributes()) {
+ if (attr.name() == QStringLiteral("api"))
+ api = attr.value().toString().trimmed();
+ else if (attr.name() == QStringLiteral("name"))
+ versionName = attr.value().toString().trimmed();
+ }
+ const bool isVulkan = api == QStringLiteral("vulkan");
+
+ while (!m_reader.atEnd()) {
+ m_reader.readNext();
+ if (m_reader.isEndElement() && m_reader.name() == QStringLiteral("feature"))
+ return;
+ if (m_reader.isStartElement() && m_reader.name() == QStringLiteral("require")) {
+ if (isVulkan)
+ parseFeatureRequire(versionName);
+ }
+ }
+}
+
+void VkSpecParser::parseFeatureRequire(const QString &versionDefine)
+{
+ // <require comment="Device initialization">
+ // <command name="vkCreateInstance"/>
+
+ while (!m_reader.atEnd()) {
+ m_reader.readNext();
+ if (m_reader.isEndElement() && m_reader.name() == QStringLiteral("require"))
+ return;
+ if (m_reader.isStartElement() && m_reader.name() == QStringLiteral("command")) {
+ for (const QXmlStreamAttribute &attr : m_reader.attributes()) {
+ if (attr.name() == QStringLiteral("name"))
+ m_versionCommandMapping[versionDefine].append(attr.value().toString().trimmed());
+ }
+ }
+ }
+}
+
void VkSpecParser::parseCommands()
{
- m_commands.clear();
+ // <commands comment="Vulkan command definitions">
+ // <command successcodes="VK_SUCCESS" ...>
while (!m_reader.atEnd()) {
m_reader.readNext();
if (m_reader.isEndElement() && m_reader.name() == QStringLiteral("commands"))
return;
- if (m_reader.isStartElement() && m_reader.name() == u"command") {
+ if (m_reader.isStartElement() && m_reader.name() == QStringLiteral("command")) {
const Command c = parseCommand();
if (!c.cmd.name.isEmpty()) // skip aliases
m_commands.append(c);
@@ -116,6 +177,12 @@ VkSpecParser::Command VkSpecParser::parseCommand()
{
Command c;
+ // <command successcodes="VK_SUCCESS" ...>
+ // <proto><type>VkResult</type> <name>vkCreateInstance</name></proto>
+ // <param>const <type>VkInstanceCreateInfo</type>* <name>pCreateInfo</name></param>
+ // <param optional="true">const <type>VkAllocationCallbacks</type>* <name>pAllocator</name></param>
+ // <param><type>VkInstance</type>* <name>pInstance</name></param>
+
while (!m_reader.atEnd()) {
m_reader.readNext();
if (m_reader.isEndElement() && m_reader.name() == QStringLiteral("command"))
@@ -265,7 +332,9 @@ QByteArray Preamble::get(const QString &fn)
return m_str;
}
-bool genVulkanFunctionsH(const QList<VkSpecParser::Command> &commands, const QString &licHeaderFn,
+bool genVulkanFunctionsH(const QList<VkSpecParser::Command> &commands,
+ const QMap<QString, QStringList> &versionCommandMapping,
+ const QString &licHeaderFn,
const QString &outputBase)
{
QFile f(outputBase + QStringLiteral(".h"));
@@ -332,11 +401,21 @@ bool genVulkanFunctionsH(const QList<VkSpecParser::Command> &commands, const QSt
QString instCmdStr;
QString devCmdStr;
- for (const VkSpecParser::Command &c : commands) {
- QString *dst = c.deviceLevel ? &devCmdStr : &instCmdStr;
- *dst += QStringLiteral(" ");
- *dst += funcSig(c);
- *dst += QStringLiteral(";\n");
+ for (const QString &version : VERSIONS) {
+ const QStringList &coreFunctionsInVersion = versionCommandMapping[version];
+ instCmdStr += "#if " + version + "\n";
+ devCmdStr += "#if " + version + "\n";
+ for (const VkSpecParser::Command &c : commands) {
+ if (!coreFunctionsInVersion.contains(c.cmd.name))
+ continue;
+
+ QString *dst = c.deviceLevel ? &devCmdStr : &instCmdStr;
+ *dst += QStringLiteral(" ");
+ *dst += funcSig(c);
+ *dst += QStringLiteral(";\n");
+ }
+ instCmdStr += "#endif\n";
+ devCmdStr += "#endif\n";
}
f.write(QString::asprintf(s, preamble.get(licHeaderFn).constData(),
@@ -346,7 +425,9 @@ bool genVulkanFunctionsH(const QList<VkSpecParser::Command> &commands, const QSt
return true;
}
-bool genVulkanFunctionsPH(const QList<VkSpecParser::Command> &commands, const QString &licHeaderFn,
+bool genVulkanFunctionsPH(const QList<VkSpecParser::Command> &commands,
+ const QMap<QString, QStringList> &versionCommandMapping,
+ const QString &licHeaderFn,
const QString &outputBase)
{
QFile f(outputBase + QStringLiteral("_p.h"));
@@ -397,16 +478,29 @@ bool genVulkanFunctionsPH(const QList<VkSpecParser::Command> &commands, const QS
"\n"
"#endif // QVULKANFUNCTIONS_P_H\n";
- const int devLevelCount = std::count_if(commands.cbegin(), commands.cend(),
- [](const VkSpecParser::Command &c) { return c.deviceLevel; });
- const int instLevelCount = commands.count() - devLevelCount;
+ int devLevelCount = 0;
+ int instLevelCount = 0;
+ for (const QString &version : VERSIONS) {
+ const QStringList &coreFunctionsInVersion = versionCommandMapping[version];
+ for (const VkSpecParser::Command &c : commands) {
+ if (!coreFunctionsInVersion.contains(c.cmd.name))
+ continue;
+
+ if (c.deviceLevel)
+ devLevelCount += 1;
+ else
+ instLevelCount += 1;
+ }
+ }
f.write(QString::asprintf(s, preamble.get(licHeaderFn).constData(), instLevelCount, devLevelCount).toUtf8());
return true;
}
-bool genVulkanFunctionsPC(const QList<VkSpecParser::Command> &commands, const QString &licHeaderFn,
+bool genVulkanFunctionsPC(const QList<VkSpecParser::Command> &commands,
+ const QMap<QString, QStringList> &versionCommandMapping,
+ const QString &licHeaderFn,
const QString &outputBase)
{
QFile f(outputBase + QStringLiteral("_p.cpp"));
@@ -429,7 +523,7 @@ bool genVulkanFunctionsPC(const QList<VkSpecParser::Command> &commands, const QS
" };\n"
" for (int i = 0; i < %d; ++i) {\n"
" m_funcs[i] = inst->getInstanceProcAddr(funcNames[i]);\n"
-" if (!m_funcs[i])\n"
+" if (i < %d && !m_funcs[i])\n"
" qWarning(\"QVulkanFunctions: Failed to resolve %%s\", funcNames[i]);\n"
" }\n"
"}\n"
@@ -443,7 +537,7 @@ bool genVulkanFunctionsPC(const QList<VkSpecParser::Command> &commands, const QS
" };\n"
" for (int i = 0; i < %d; ++i) {\n"
" m_funcs[i] = f->vkGetDeviceProcAddr(device, funcNames[i]);\n"
-" if (!m_funcs[i])\n"
+" if (i < %d && !m_funcs[i])\n"
" qWarning(\"QVulkanDeviceFunctions: Failed to resolve %%s\", funcNames[i]);\n"
" }\n"
"}\n"
@@ -453,23 +547,46 @@ bool genVulkanFunctionsPC(const QList<VkSpecParser::Command> &commands, const QS
QString devCmdWrapperStr;
QString instCmdWrapperStr;
int devIdx = 0;
+ int devCount = 0;
int instIdx = 0;
+ int instCount = 0;
QString devCmdNamesStr;
QString instCmdNamesStr;
-
- for (int i = 0; i < commands.count(); ++i) {
- QString *dst = commands[i].deviceLevel ? &devCmdWrapperStr : &instCmdWrapperStr;
- int *idx = commands[i].deviceLevel ? &devIdx : &instIdx;
- *dst += funcSig(commands[i], commands[i].deviceLevel ? "QVulkanDeviceFunctions" : "QVulkanFunctions");
- *dst += QString(QStringLiteral("\n{\n Q_ASSERT(d_ptr->m_funcs[%1]);\n ")).arg(*idx);
- *dst += funcCall(commands[i], *idx);
- *dst += QStringLiteral(";\n}\n\n");
- ++*idx;
-
- dst = commands[i].deviceLevel ? &devCmdNamesStr : &instCmdNamesStr;
- *dst += QStringLiteral(" \"");
- *dst += commands[i].cmd.name;
- *dst += QStringLiteral("\",\n");
+ int vulkan10DevCount = 0;
+ int vulkan10InstCount = 0;
+
+ for (const QString &version : VERSIONS) {
+ const QStringList &coreFunctionsInVersion = versionCommandMapping[version];
+ instCmdWrapperStr += "\n#if " + version + "\n";
+ devCmdWrapperStr += "\n#if " + version + "\n";
+ for (const VkSpecParser::Command &c : commands) {
+ if (!coreFunctionsInVersion.contains(c.cmd.name))
+ continue;
+
+ QString *dst = c.deviceLevel ? &devCmdWrapperStr : &instCmdWrapperStr;
+ int *idx = c.deviceLevel ? &devIdx : &instIdx;
+ *dst += funcSig(c, c.deviceLevel ? "QVulkanDeviceFunctions" : "QVulkanFunctions");
+ *dst += QString(QStringLiteral("\n{\n Q_ASSERT(d_ptr->m_funcs[%1]);\n ")).arg(*idx);
+ *dst += funcCall(c, *idx);
+ *dst += QStringLiteral(";\n}\n\n");
+ *idx += 1;
+
+ dst = c.deviceLevel ? &devCmdNamesStr : &instCmdNamesStr;
+ *dst += QStringLiteral(" \"");
+ *dst += c.cmd.name;
+ *dst += QStringLiteral("\",\n");
+
+ if (c.deviceLevel)
+ devCount += 1;
+ else
+ instCount += 1;
+ }
+ if (version == QStringLiteral("VK_VERSION_1_0")) {
+ vulkan10InstCount = instIdx;
+ vulkan10DevCount = devIdx;
+ }
+ instCmdWrapperStr += "#endif\n\n";
+ devCmdWrapperStr += "#endif\n\n";
}
if (devCmdNamesStr.count() > 2)
@@ -480,9 +597,9 @@ bool genVulkanFunctionsPC(const QList<VkSpecParser::Command> &commands, const QS
const QString str =
QString::asprintf(s, preamble.get(licHeaderFn).constData(),
instCmdWrapperStr.toUtf8().constData(),
- instCmdNamesStr.toUtf8().constData(), instIdx,
+ instCmdNamesStr.toUtf8().constData(), instCount, vulkan10InstCount,
devCmdWrapperStr.toUtf8().constData(),
- devCmdNamesStr.toUtf8().constData(), commands.count() - instIdx);
+ devCmdNamesStr.toUtf8().constData(), devCount, vulkan10DevCount);
f.write(str.toUtf8());
@@ -505,27 +622,27 @@ int main(int argc, char **argv)
if (!parser.parse())
return 1;
+ // Now we have a list of functions (commands), including extensions, and a
+ // table of Version (1.0, 1.1, 1.2) -> Core functions in that version.
QList<VkSpecParser::Command> commands = parser.commands();
+ QMap<QString, QStringList> versionCommandMapping = parser.versionCommandMapping();
+
QStringList ignoredFuncs {
QStringLiteral("vkCreateInstance"),
QStringLiteral("vkDestroyInstance"),
- QStringLiteral("vkGetInstanceProcAddr")
+ QStringLiteral("vkGetInstanceProcAddr"),
+ QStringLiteral("vkEnumerateInstanceVersion")
};
-
- // Filter out extensions and unwanted functions.
- // The check for the former is rather simplistic for now: skip if the last letter is uppercase...
for (int i = 0; i < commands.count(); ++i) {
- QString name = commands[i].cmd.name;
- QChar c = name[name.count() - 1];
- if (c.isUpper() || ignoredFuncs.contains(name))
+ if (ignoredFuncs.contains(commands[i].cmd.name))
commands.remove(i--);
}
QString licenseHeaderFileName = QString::fromUtf8(argv[2]);
QString outputBase = QString::fromUtf8(argv[3]);
- genVulkanFunctionsH(commands, licenseHeaderFileName, outputBase);
- genVulkanFunctionsPH(commands, licenseHeaderFileName, outputBase);
- genVulkanFunctionsPC(commands, licenseHeaderFileName, outputBase);
+ genVulkanFunctionsH(commands, versionCommandMapping, licenseHeaderFileName, outputBase);
+ genVulkanFunctionsPH(commands, versionCommandMapping, licenseHeaderFileName, outputBase);
+ genVulkanFunctionsPC(commands, versionCommandMapping, licenseHeaderFileName, outputBase);
return 0;
}