4#include <QtCore/qcoreapplication.h>
5#include <QtCore/qfile.h>
6#include <QtCore/qfileinfo.h>
7#include <QtCore/qlist.h>
8#include <QtCore/qmap.h>
9#include <QtCore/qxmlstream.h>
13 QStringLiteral(
"VK_VERSION_1_0"),
14 QStringLiteral(
"VK_VERSION_1_1"),
15 QStringLiteral(
"VK_VERSION_1_2"),
16 QStringLiteral(
"VK_VERSION_1_3"),
17 QStringLiteral(
"VK_VERSION_1_4")
45 void parseFeatureRequire(
const QString &versionDefine);
48 TypedName parseParamOrProto(
const QString &tag);
52 QXmlStreamReader m_reader;
53 QList<Command> m_commands;
54 QMap<QString, QStringList> m_versionCommandMapping;
60 m_file.setFileName(m_fn);
61 if (!m_file.open(QIODevice::ReadOnly | QIODevice::Text)) {
62 qWarning(
"Failed to open %s", qPrintable(m_file.fileName()));
65 m_reader.setDevice(&m_file);
68 m_versionCommandMapping.clear();
70 while (!m_reader.atEnd()) {
72 if (m_reader.isStartElement()) {
73 if (m_reader.name() == QStringLiteral(
"commands"))
75 else if (m_reader.name() == QStringLiteral(
"feature"))
85 QString tag = m_reader.name().toString();
86 while (!m_reader.atEnd()) {
88 if (m_reader.isEndElement() && m_reader.name() == tag)
100 for (
const QXmlStreamAttribute &attr : m_reader.attributes()) {
101 if (attr.name() == QStringLiteral(
"api"))
102 api = attr.value().toString().trimmed();
103 else if (attr.name() == QStringLiteral(
"name"))
104 versionName = attr.value().toString().trimmed();
106 const bool isVulkan = api.split(
',').contains(QStringLiteral(
"vulkan"));
108 while (!m_reader.atEnd()) {
110 if (m_reader.isEndElement() && m_reader.name() == QStringLiteral(
"feature"))
112 if (m_reader.isStartElement() && m_reader.name() == QStringLiteral(
"require")) {
114 parseFeatureRequire(versionName);
119void VkSpecParser::parseFeatureRequire(
const QString &versionDefine)
124 while (!m_reader.atEnd()) {
126 if (m_reader.isEndElement() && m_reader.name() == QStringLiteral(
"require"))
128 if (m_reader.isStartElement() && m_reader.name() == QStringLiteral(
"command")) {
129 for (
const QXmlStreamAttribute &attr : m_reader.attributes()) {
130 if (attr.name() == QStringLiteral(
"name"))
131 m_versionCommandMapping[versionDefine].append(attr.value().toString().trimmed());
142 while (!m_reader.atEnd()) {
144 if (m_reader.isEndElement() && m_reader.name() == QStringLiteral(
"commands"))
146 if (m_reader.isStartElement() && m_reader.name() == QStringLiteral(
"command")) {
147 const Command c = parseCommand();
148 if (!c.cmd.name.isEmpty())
149 m_commands.append(c);
165 for (
const QXmlStreamAttribute &attr : m_reader.attributes()) {
166 if (attr.name() == QStringLiteral(
"api"))
167 api = attr.value().toString().trimmed();
170 if (!api.isEmpty() && !api.split(
',').contains(QStringLiteral(
"vulkan"))) {
175 while (!m_reader.atEnd()) {
177 if (m_reader.isEndElement() && m_reader.name() == QStringLiteral(
"command"))
179 if (m_reader.isStartElement()) {
180 const QString protoStr = QStringLiteral(
"proto");
181 const QString paramStr = QStringLiteral(
"param");
182 if (m_reader.name() == protoStr) {
183 c.cmd = parseParamOrProto(protoStr);
184 }
else if (m_reader.name() == paramStr) {
185 c.args.append(parseParamOrProto(paramStr));
193 if (!c.args.isEmpty()) {
194 QStringList dispatchableDeviceAndChildTypes {
195 QStringLiteral(
"VkDevice"),
196 QStringLiteral(
"VkQueue"),
197 QStringLiteral(
"VkCommandBuffer")
199 if (dispatchableDeviceAndChildTypes.contains(c.args[0].type)
200 && c.cmd.name != QStringLiteral(
"vkGetDeviceProcAddr"))
213 while (!m_reader.atEnd()) {
215 if (m_reader.isEndElement() && m_reader.name() == tag)
217 if (m_reader.isStartElement()) {
218 if (m_reader.name() == QStringLiteral(
"name")) {
219 t.name = parseName();
220 }
else if (m_reader.name() != QStringLiteral(
"type")) {
224 auto text = m_reader.text().trimmed();
225 if (!text.isEmpty()) {
226 if (text.startsWith(u'[')) {
227 t.typeSuffix += text;
229 if (!t.type.isEmpty())
243 while (!m_reader.atEnd()) {
245 if (m_reader.isEndElement() && m_reader.name() == QStringLiteral(
"name"))
247 name += m_reader.text();
249 return name.trimmed();
254 QString s(QString::asprintf(
"%s %s%s%s", qPrintable(c.cmd.type),
255 (className ? className :
""), (className ?
"::" :
""),
256 qPrintable(c.cmd.name)));
257 if (!c.args.isEmpty()) {
260 for (
const VkSpecParser::TypedName &a : c.args) {
262 s += QStringLiteral(
", ");
265 s += QString::asprintf(
"%s%s%s%s", qPrintable(a.type),
266 (a.type.endsWith(u'*') ?
"" :
" "),
267 qPrintable(a.name), qPrintable(a.typeSuffix));
278 QString s = QString::asprintf(
"%sreinterpret_cast<PFN_%s>(d_ptr->m_funcs[%d])",
279 (c.cmd.type == QStringLiteral(
"void") ?
"" :
"return "),
280 qPrintable(c.cmd.name),
282 if (!c.args.isEmpty()) {
285 for (
const VkSpecParser::TypedName &a : c.args) {
287 s += QStringLiteral(
", ");
308 if (!m_str.isEmpty())
312 if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) {
313 qWarning(
"Failed to open %s", qPrintable(fn));
318 m_str.replace(
"FOO",
"QtGui");
319 m_str +=
"\n// This file is automatically generated by qvkgen. Do not edit.\n";
325 const QMap<QString, QStringList> &versionCommandMapping,
326 const QString &licHeaderFn,
327 const QString &outputBase)
329 QFile f(outputBase + QStringLiteral(
".h"));
330 if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) {
331 qWarning(
"Failed to write %s", qPrintable(f.fileName()));
335 static const char *s =
337"#ifndef QVULKANFUNCTIONS_H\n"
338"#define QVULKANFUNCTIONS_H\n"
341"#pragma qt_no_master_include\n"
344"#include <QtGui/qtguiglobal.h>\n"
346"#if QT_CONFIG(vulkan) || defined(Q_QDOC)\n"
348"#ifndef VK_NO_PROTOTYPES\n"
349"#define VK_NO_PROTOTYPES\n"
351"#include <vulkan/vulkan.h>\n"
353"#include <QtCore/qscopedpointer.h>\n"
355"QT_BEGIN_NAMESPACE\n"
357"class QVulkanInstance;\n"
358"class QVulkanFunctionsPrivate;\n"
359"class QVulkanDeviceFunctionsPrivate;\n"
361"class Q_GUI_EXPORT QVulkanFunctions\n"
364" ~QVulkanFunctions();\n"
368" Q_DISABLE_COPY(QVulkanFunctions)\n"
369" QVulkanFunctions(QVulkanInstance *inst);\n"
371" QScopedPointer<QVulkanFunctionsPrivate> d_ptr;\n"
372" friend class QVulkanInstance;\n"
375"class Q_GUI_EXPORT QVulkanDeviceFunctions\n"
378" ~QVulkanDeviceFunctions();\n"
382" Q_DISABLE_COPY(QVulkanDeviceFunctions)\n"
383" QVulkanDeviceFunctions(QVulkanInstance *inst, VkDevice device);\n"
385" QScopedPointer<QVulkanDeviceFunctionsPrivate> d_ptr;\n"
386" friend class QVulkanInstance;\n"
391"#endif // QT_CONFIG(vulkan) || defined(Q_QDOC)\n"
393"#endif // QVULKANFUNCTIONS_H\n";
397 for (
const QString &version : VERSIONS) {
398 const QStringList &coreFunctionsInVersion = versionCommandMapping[version];
399 instCmdStr +=
"#if " + version +
"\n";
400 devCmdStr +=
"#if " + version +
"\n";
401 for (
const VkSpecParser::Command &c : commands) {
402 if (!coreFunctionsInVersion.contains(c.cmd.name))
405 QString *dst = c.deviceLevel ? &devCmdStr : &instCmdStr;
406 *dst += QStringLiteral(
" ");
408 *dst += QStringLiteral(
";\n");
410 instCmdStr +=
"#endif\n";
411 devCmdStr +=
"#endif\n";
414 f.write(QString::asprintf(s, preamble.get(licHeaderFn).constData(),
415 instCmdStr.toUtf8().constData(),
416 devCmdStr.toUtf8().constData()).toUtf8());
422 const QMap<QString, QStringList> &versionCommandMapping,
423 const QString &licHeaderFn,
424 const QString &outputBase)
426 QFile f(outputBase + QStringLiteral(
"_p.h"));
427 if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) {
428 qWarning(
"Failed to write %s", qPrintable(f.fileName()));
432 static const char *s =
434"#ifndef QVULKANFUNCTIONS_P_H\n"
435"#define QVULKANFUNCTIONS_P_H\n"
441"// This file is not part of the Qt API. It exists purely as an\n"
442"// implementation detail. This header file may change from version to\n"
443"// version without notice, or even be removed.\n"
448"#include \"qvulkanfunctions.h\"\n"
450"QT_BEGIN_NAMESPACE\n"
452"class QVulkanInstance;\n"
454"class QVulkanFunctionsPrivate\n"
457" QVulkanFunctionsPrivate(QVulkanInstance *inst);\n"
459" PFN_vkVoidFunction m_funcs[%d];\n"
462"class QVulkanDeviceFunctionsPrivate\n"
465" QVulkanDeviceFunctionsPrivate(QVulkanInstance *inst, VkDevice device);\n"
467" PFN_vkVoidFunction m_funcs[%d];\n"
472"#endif // QVULKANFUNCTIONS_P_H\n";
474 int devLevelCount = 0;
475 int instLevelCount = 0;
476 for (
const QString &version : VERSIONS) {
477 const QStringList &coreFunctionsInVersion = versionCommandMapping[version];
478 for (
const VkSpecParser::Command &c : commands) {
479 if (!coreFunctionsInVersion.contains(c.cmd.name))
489 f.write(QString::asprintf(s, preamble.get(licHeaderFn).constData(), instLevelCount, devLevelCount).toUtf8());
495 const QMap<QString, QStringList> &versionCommandMapping,
496 const QString &licHeaderFn,
497 const QString &outputBase)
499 QFile f(outputBase + QStringLiteral(
"_p.cpp"));
500 if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) {
501 qWarning(
"Failed to write %s", qPrintable(f.fileName()));
505 static const char s[] =
507"#include \"qvulkanfunctions_p.h\"\n"
508"#include \"qvulkaninstance.h\"\n"
510"#include <QtCore/private/qoffsetstringarray_p.h>\n"
512"QT_BEGIN_NAMESPACE\n"
514"QVulkanFunctionsPrivate::QVulkanFunctionsPrivate(QVulkanInstance *inst)\n"
516" static constexpr auto funcNames = qOffsetStringArray(\n"
519" static_assert(std::extent_v<decltype(m_funcs)> == size_t(funcNames.count()));\n"
520" for (int i = 0; i < funcNames.count(); ++i) {\n"
521" m_funcs[i] = inst->getInstanceProcAddr(funcNames.at(i));\n"
522" if (i < %d && !m_funcs[i])\n"
523" qWarning(\"QVulkanFunctions: Failed to resolve %%s\", funcNames.at(i));\n"
527"QVulkanDeviceFunctionsPrivate::QVulkanDeviceFunctionsPrivate(QVulkanInstance *inst, VkDevice device)\n"
529" QVulkanFunctions *f = inst->functions();\n"
531" static constexpr auto funcNames = qOffsetStringArray(\n"
534" static_assert(std::extent_v<decltype(m_funcs)> == size_t(funcNames.count()));\n"
535" for (int i = 0; i < funcNames.count(); ++i) {\n"
536" m_funcs[i] = f->vkGetDeviceProcAddr(device, funcNames.at(i));\n"
537" if (i < %d && !m_funcs[i])\n"
538" qWarning(\"QVulkanDeviceFunctions: Failed to resolve %%s\", funcNames.at(i));\n"
544 QString devCmdWrapperStr;
545 QString instCmdWrapperStr;
548 QString devCmdNamesStr;
549 QString instCmdNamesStr;
550 int vulkan10DevCount = 0;
551 int vulkan10InstCount = 0;
553 for (
const QString &version : VERSIONS) {
554 const QStringList &coreFunctionsInVersion = versionCommandMapping[version];
555 instCmdWrapperStr +=
"\n#if " + version +
"\n";
556 devCmdWrapperStr +=
"\n#if " + version +
"\n";
557 for (
const VkSpecParser::Command &c : commands) {
558 if (!coreFunctionsInVersion.contains(c.cmd.name))
561 QString *dst = c.deviceLevel ? &devCmdWrapperStr : &instCmdWrapperStr;
562 int *idx = c.deviceLevel ? &devIdx : &instIdx;
563 *dst += funcSig(c, c.deviceLevel ?
"QVulkanDeviceFunctions" :
"QVulkanFunctions");
564 *dst += QString(QStringLiteral(
"\n{\n Q_ASSERT(d_ptr->m_funcs[%1]);\n ")).arg(*idx);
565 *dst += funcCall(c, *idx);
566 *dst += QStringLiteral(
";\n}\n\n");
569 dst = c.deviceLevel ? &devCmdNamesStr : &instCmdNamesStr;
570 *dst += QStringLiteral(
" \"");
572 *dst += QStringLiteral(
"\",\n");
574 if (version == QStringLiteral(
"VK_VERSION_1_0")) {
575 vulkan10InstCount = instIdx;
576 vulkan10DevCount = devIdx;
578 instCmdWrapperStr +=
"#endif\n\n";
579 devCmdWrapperStr +=
"#endif\n\n";
582 if (devCmdNamesStr.size() > 2)
583 devCmdNamesStr.chop(2);
584 if (instCmdNamesStr.size() > 2)
585 instCmdNamesStr.chop(2);
588 QString::asprintf(s, preamble.get(licHeaderFn).constData(),
589 instCmdWrapperStr.toUtf8().constData(),
590 instCmdNamesStr.toUtf8().constData(), vulkan10InstCount,
591 devCmdWrapperStr.toUtf8().constData(),
592 devCmdNamesStr.toUtf8().constData(), vulkan10DevCount);
594 f.write(str.toUtf8());
601 QCoreApplication app(argc, argv);
605 qWarning(
"Usage: qvkgen input_vk_xml input_license_header output_base\n"
606 " For example: qvkgen vulkan/vk.xml vulkan/qvulkanfunctions.header vulkan/qvulkanfunctions");
610 parser.setFileName(QString::fromUtf8(argv[1]));
617 QList<VkSpecParser::Command> commands = parser.commands();
618 QMap<QString, QStringList> versionCommandMapping = parser.versionCommandMapping();
620 QStringList ignoredFuncs {
621 QStringLiteral(
"vkCreateInstance"),
622 QStringLiteral(
"vkDestroyInstance"),
623 QStringLiteral(
"vkGetInstanceProcAddr"),
624 QStringLiteral(
"vkEnumerateInstanceVersion")
626 for (
int i = 0; i < commands.size(); ++i) {
627 if (ignoredFuncs.contains(commands[i].cmd.name))
628 commands.remove(i--);
631 QString licenseHeaderFileName = QString::fromUtf8(argv[2]);
632 QString outputBase = QString::fromUtf8(argv[3]);
633 genVulkanFunctionsH(commands, versionCommandMapping, licenseHeaderFileName, outputBase);
634 genVulkanFunctionsPH(commands, versionCommandMapping, licenseHeaderFileName, outputBase);
635 genVulkanFunctionsPC(commands, versionCommandMapping, licenseHeaderFileName, outputBase);
QByteArray get(const QString &fn)
void setFileName(const QString &fn)
QList< Command > commands() const
QMap< QString, QStringList > versionCommandMapping() const
static const QStringList VERSIONS
QString funcCall(const VkSpecParser::Command &c, int idx)
bool genVulkanFunctionsH(const QList< VkSpecParser::Command > &commands, const QMap< QString, QStringList > &versionCommandMapping, const QString &licHeaderFn, const QString &outputBase)
bool genVulkanFunctionsPH(const QList< VkSpecParser::Command > &commands, const QMap< QString, QStringList > &versionCommandMapping, const QString &licHeaderFn, const QString &outputBase)
bool genVulkanFunctionsPC(const QList< VkSpecParser::Command > &commands, const QMap< QString, QStringList > &versionCommandMapping, const QString &licHeaderFn, const QString &outputBase)
QString funcSig(const VkSpecParser::Command &c, const char *className=nullptr)
int main(int argc, char *argv[])
[ctor_close]