Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qvkgen.cpp
Go to the documentation of this file.
1// Copyright (C) 2017 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
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>
10
11// generate wrappers for core functions from the following versions
12static const QStringList VERSIONS = {
13 QStringLiteral("VK_VERSION_1_0"), // must be the first and always present
14 QStringLiteral("VK_VERSION_1_1"),
15 QStringLiteral("VK_VERSION_1_2"),
16 QStringLiteral("VK_VERSION_1_3"),
17 QStringLiteral("VK_VERSION_1_4")
18};
19
21{
22public:
23 bool parse();
24
30
36
37 QList<Command> commands() const { return m_commands; }
38 QMap<QString, QStringList> versionCommandMapping() const { return m_versionCommandMapping; }
39
40 void setFileName(const QString &fn) { m_fn = fn; }
41
42private:
43 void skip();
44 void parseFeature();
45 void parseFeatureRequire(const QString &versionDefine);
46 void parseCommands();
47 Command parseCommand();
48 TypedName parseParamOrProto(const QString &tag);
49 QString parseName();
50
51 QFile m_file;
52 QXmlStreamReader m_reader;
53 QList<Command> m_commands;
54 QMap<QString, QStringList> m_versionCommandMapping; // "1.0" -> ["vkGetPhysicalDeviceProperties", ...]
55 QString m_fn;
56};
57
59{
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()));
63 return false;
64 }
65 m_reader.setDevice(&m_file);
66
67 m_commands.clear();
68 m_versionCommandMapping.clear();
69
70 while (!m_reader.atEnd()) {
71 m_reader.readNext();
72 if (m_reader.isStartElement()) {
73 if (m_reader.name() == QStringLiteral("commands"))
74 parseCommands();
75 else if (m_reader.name() == QStringLiteral("feature"))
76 parseFeature();
77 }
78 }
79
80 return true;
81}
82
83void VkSpecParser::skip()
84{
85 QString tag = m_reader.name().toString();
86 while (!m_reader.atEnd()) {
87 m_reader.readNext();
88 if (m_reader.isEndElement() && m_reader.name() == tag)
89 break;
90 }
91}
92
93void VkSpecParser::parseFeature()
94{
95 // <feature api="vulkan" name="VK_VERSION_1_0" number="1.0" comment="Vulkan core API interface definitions">
96 // <require comment="Device initialization">
97
98 QString api;
99 QString versionName;
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();
105 }
106 const bool isVulkan = api.split(',').contains(QStringLiteral("vulkan"));
107
108 while (!m_reader.atEnd()) {
109 m_reader.readNext();
110 if (m_reader.isEndElement() && m_reader.name() == QStringLiteral("feature"))
111 return;
112 if (m_reader.isStartElement() && m_reader.name() == QStringLiteral("require")) {
113 if (isVulkan)
114 parseFeatureRequire(versionName);
115 }
116 }
117}
118
119void VkSpecParser::parseFeatureRequire(const QString &versionDefine)
120{
121 // <require comment="Device initialization">
122 // <command name="vkCreateInstance"/>
123
124 while (!m_reader.atEnd()) {
125 m_reader.readNext();
126 if (m_reader.isEndElement() && m_reader.name() == QStringLiteral("require"))
127 return;
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());
132 }
133 }
134 }
135}
136
137void VkSpecParser::parseCommands()
138{
139 // <commands comment="Vulkan command definitions">
140 // <command successcodes="VK_SUCCESS" ...>
141
142 while (!m_reader.atEnd()) {
143 m_reader.readNext();
144 if (m_reader.isEndElement() && m_reader.name() == QStringLiteral("commands"))
145 return;
146 if (m_reader.isStartElement() && m_reader.name() == QStringLiteral("command")) {
147 const Command c = parseCommand();
148 if (!c.cmd.name.isEmpty()) // skip aliases and commands for another api
149 m_commands.append(c);
150 }
151 }
152}
153
154VkSpecParser::Command VkSpecParser::parseCommand()
155{
156 Command c;
157
158 // <command successcodes="VK_SUCCESS" ...>
159 // <proto><type>VkResult</type> <name>vkCreateInstance</name></proto>
160 // <param>const <type>VkInstanceCreateInfo</type>* <name>pCreateInfo</name></param>
161 // <param optional="true">const <type>VkAllocationCallbacks</type>* <name>pAllocator</name></param>
162 // <param><type>VkInstance</type>* <name>pInstance</name></param>
163
164 QString api;
165 for (const QXmlStreamAttribute &attr : m_reader.attributes()) {
166 if (attr.name() == QStringLiteral("api"))
167 api = attr.value().toString().trimmed();
168 }
169 // skip commands with api="vulkansc", but the api attribute is optional
170 if (!api.isEmpty() && !api.split(',').contains(QStringLiteral("vulkan"))) {
171 skip();
172 return {};
173 }
174
175 while (!m_reader.atEnd()) {
176 m_reader.readNext();
177 if (m_reader.isEndElement() && m_reader.name() == QStringLiteral("command"))
178 break;
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));
186 } else {
187 skip();
188 }
189 }
190 }
191
192 c.deviceLevel = false;
193 if (!c.args.isEmpty()) {
194 QStringList dispatchableDeviceAndChildTypes {
195 QStringLiteral("VkDevice"),
196 QStringLiteral("VkQueue"),
197 QStringLiteral("VkCommandBuffer")
198 };
199 if (dispatchableDeviceAndChildTypes.contains(c.args[0].type)
200 && c.cmd.name != QStringLiteral("vkGetDeviceProcAddr"))
201 {
202 c.deviceLevel = true;
203 }
204 }
205
206 return c;
207}
208
209VkSpecParser::TypedName VkSpecParser::parseParamOrProto(const QString &tag)
210{
211 TypedName t;
212
213 while (!m_reader.atEnd()) {
214 m_reader.readNext();
215 if (m_reader.isEndElement() && m_reader.name() == tag)
216 break;
217 if (m_reader.isStartElement()) {
218 if (m_reader.name() == QStringLiteral("name")) {
219 t.name = parseName();
220 } else if (m_reader.name() != QStringLiteral("type")) {
221 skip();
222 }
223 } else {
224 auto text = m_reader.text().trimmed();
225 if (!text.isEmpty()) {
226 if (text.startsWith(u'[')) {
227 t.typeSuffix += text;
228 } else {
229 if (!t.type.isEmpty())
230 t.type += u' ';
231 t.type += text;
232 }
233 }
234 }
235 }
236
237 return t;
238}
239
240QString VkSpecParser::parseName()
241{
242 QString name;
243 while (!m_reader.atEnd()) {
244 m_reader.readNext();
245 if (m_reader.isEndElement() && m_reader.name() == QStringLiteral("name"))
246 break;
247 name += m_reader.text();
248 }
249 return name.trimmed();
250}
251
252QString funcSig(const VkSpecParser::Command &c, const char *className = nullptr)
253{
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()) {
258 s += u'(';
259 bool first = true;
260 for (const VkSpecParser::TypedName &a : c.args) {
261 if (!first)
262 s += QStringLiteral(", ");
263 else
264 first = false;
265 s += QString::asprintf("%s%s%s%s", qPrintable(a.type),
266 (a.type.endsWith(u'*') ? "" : " "),
267 qPrintable(a.name), qPrintable(a.typeSuffix));
268 }
269 s += u')';
270 }
271 return s;
272}
273
275{
276 // template:
277 // [return] reinterpret_cast<PFN_vkEnumeratePhysicalDevices>(d_ptr->m_funcs[0])(instance, pPhysicalDeviceCount, pPhysicalDevices);
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),
281 idx);
282 if (!c.args.isEmpty()) {
283 s += u'(';
284 bool first = true;
285 for (const VkSpecParser::TypedName &a : c.args) {
286 if (!first)
287 s += QStringLiteral(", ");
288 else
289 first = false;
290 s += a.name;
291 }
292 s += u')';
293 }
294 return s;
295}
296
298{
299public:
300 QByteArray get(const QString &fn);
301
302private:
303 QByteArray m_str;
304} preamble;
305
306QByteArray Preamble::get(const QString &fn)
307{
308 if (!m_str.isEmpty())
309 return m_str;
310
311 QFile f(fn);
312 if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) {
313 qWarning("Failed to open %s", qPrintable(fn));
314 return m_str;
315 }
316
317 m_str = f.readAll();
318 m_str.replace("FOO", "QtGui");
319 m_str += "\n// This file is automatically generated by qvkgen. Do not edit.\n";
320
321 return m_str;
322}
323
324bool genVulkanFunctionsH(const QList<VkSpecParser::Command> &commands,
325 const QMap<QString, QStringList> &versionCommandMapping,
326 const QString &licHeaderFn,
327 const QString &outputBase)
328{
329 QFile f(outputBase + QStringLiteral(".h"));
330 if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) {
331 qWarning("Failed to write %s", qPrintable(f.fileName()));
332 return false;
333 }
334
335 static const char *s =
336"%s\n"
337"#ifndef QVULKANFUNCTIONS_H\n"
338"#define QVULKANFUNCTIONS_H\n"
339"\n"
340"#if 0\n"
341"#pragma qt_no_master_include\n"
342"#endif\n"
343"\n"
344"#include <QtGui/qtguiglobal.h>\n"
345"\n"
346"#if (QT_CONFIG(vulkan) && __has_include(<vulkan/vulkan.h>)) || defined(Q_QDOC)\n"
347"\n"
348"#ifndef VK_NO_PROTOTYPES\n"
349"#define VK_NO_PROTOTYPES\n"
350"#endif\n"
351"#include <vulkan/vulkan.h>\n"
352"\n"
353"#include <QtCore/qscopedpointer.h>\n"
354"\n"
355"QT_BEGIN_NAMESPACE\n"
356"\n"
357"class QVulkanInstance;\n"
358"class QVulkanFunctionsPrivate;\n"
359"class QVulkanDeviceFunctionsPrivate;\n"
360"\n"
361"class Q_GUI_EXPORT QVulkanFunctions\n"
362"{\n"
363"public:\n"
364" ~QVulkanFunctions();\n"
365"\n"
366"%s\n"
367"private:\n"
368" Q_DISABLE_COPY(QVulkanFunctions)\n"
369" QVulkanFunctions(QVulkanInstance *inst);\n"
370"\n"
371" QScopedPointer<QVulkanFunctionsPrivate> d_ptr;\n"
372" friend class QVulkanInstance;\n"
373"};\n"
374"\n"
375"class Q_GUI_EXPORT QVulkanDeviceFunctions\n"
376"{\n"
377"public:\n"
378" ~QVulkanDeviceFunctions();\n"
379"\n"
380"%s\n"
381"private:\n"
382" Q_DISABLE_COPY(QVulkanDeviceFunctions)\n"
383" QVulkanDeviceFunctions(QVulkanInstance *inst, VkDevice device);\n"
384"\n"
385" QScopedPointer<QVulkanDeviceFunctionsPrivate> d_ptr;\n"
386" friend class QVulkanInstance;\n"
387"};\n"
388"\n"
389"QT_END_NAMESPACE\n"
390"\n"
391"#endif // QT_CONFIG(vulkan) && __has_include(<vulkan/vulkan.h>)\n"
392"\n"
393"#endif // QVULKANFUNCTIONS_H\n";
394
395 QString instCmdStr;
396 QString devCmdStr;
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))
403 continue;
404
405 QString *dst = c.deviceLevel ? &devCmdStr : &instCmdStr;
406 *dst += QStringLiteral(" ");
407 *dst += funcSig(c);
408 *dst += QStringLiteral(";\n");
409 }
410 instCmdStr += "#endif\n";
411 devCmdStr += "#endif\n";
412 }
413
414 f.write(QString::asprintf(s, preamble.get(licHeaderFn).constData(),
415 instCmdStr.toUtf8().constData(),
416 devCmdStr.toUtf8().constData()).toUtf8());
417
418 return true;
419}
420
421bool genVulkanFunctionsPH(const QList<VkSpecParser::Command> &commands,
422 const QMap<QString, QStringList> &versionCommandMapping,
423 const QString &licHeaderFn,
424 const QString &outputBase)
425{
426 QFile f(outputBase + QStringLiteral("_p.h"));
427 if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) {
428 qWarning("Failed to write %s", qPrintable(f.fileName()));
429 return false;
430 }
431
432 static const char *s =
433"%s\n"
434"#ifndef QVULKANFUNCTIONS_P_H\n"
435"#define QVULKANFUNCTIONS_P_H\n"
436"\n"
437"//\n"
438"// W A R N I N G\n"
439"// -------------\n"
440"//\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"
444"//\n"
445"// We mean it.\n"
446"//\n"
447"\n"
448"#include \"qvulkanfunctions.h\"\n"
449"\n"
450"#if QT_CONFIG(vulkan) && __has_include(<vulkan/vulkan.h>)\n"
451"\n"
452"QT_BEGIN_NAMESPACE\n"
453"\n"
454"class QVulkanInstance;\n"
455"\n"
456"class QVulkanFunctionsPrivate\n"
457"{\n"
458"public:\n"
459" QVulkanFunctionsPrivate(QVulkanInstance *inst);\n"
460"\n"
461" PFN_vkVoidFunction m_funcs[%d];\n"
462"};\n"
463"\n"
464"class QVulkanDeviceFunctionsPrivate\n"
465"{\n"
466"public:\n"
467" QVulkanDeviceFunctionsPrivate(QVulkanInstance *inst, VkDevice device);\n"
468"\n"
469" PFN_vkVoidFunction m_funcs[%d];\n"
470"};\n"
471"\n"
472"QT_END_NAMESPACE\n"
473"\n"
474"#endif // QT_CONFIG(vulkan) && __has_include(<vulkan/vulkan.h>)\n"
475"\n"
476"#endif // QVULKANFUNCTIONS_P_H\n";
477
478 int devLevelCount = 0;
479 int instLevelCount = 0;
480 for (const QString &version : VERSIONS) {
481 const QStringList &coreFunctionsInVersion = versionCommandMapping[version];
482 for (const VkSpecParser::Command &c : commands) {
483 if (!coreFunctionsInVersion.contains(c.cmd.name))
484 continue;
485
486 if (c.deviceLevel)
487 devLevelCount += 1;
488 else
489 instLevelCount += 1;
490 }
491 }
492
493 f.write(QString::asprintf(s, preamble.get(licHeaderFn).constData(), instLevelCount, devLevelCount).toUtf8());
494
495 return true;
496}
497
498bool genVulkanFunctionsPC(const QList<VkSpecParser::Command> &commands,
499 const QMap<QString, QStringList> &versionCommandMapping,
500 const QString &licHeaderFn,
501 const QString &outputBase)
502{
503 QFile f(outputBase + QStringLiteral("_p.cpp"));
504 if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) {
505 qWarning("Failed to write %s", qPrintable(f.fileName()));
506 return false;
507 }
508
509 static const char s[] =
510"%s\n"
511"#include \"qvulkanfunctions_p.h\"\n"
512"#include \"qvulkaninstance.h\"\n"
513"\n"
514"#include <QtCore/private/qoffsetstringarray_p.h>\n"
515"\n"
516"QT_BEGIN_NAMESPACE\n"
517"\n%s"
518"QVulkanFunctionsPrivate::QVulkanFunctionsPrivate(QVulkanInstance *inst)\n"
519"{\n"
520" static constexpr auto funcNames = qOffsetStringArray(\n"
521"%s\n"
522" );\n"
523" static_assert(std::extent_v<decltype(m_funcs)> == size_t(funcNames.count()));\n"
524" for (int i = 0; i < funcNames.count(); ++i) {\n"
525" m_funcs[i] = inst->getInstanceProcAddr(funcNames.at(i));\n"
526" if (i < %d && !m_funcs[i])\n"
527" qWarning(\"QVulkanFunctions: Failed to resolve %%s\", funcNames.at(i));\n"
528" }\n"
529"}\n"
530"\n%s"
531"QVulkanDeviceFunctionsPrivate::QVulkanDeviceFunctionsPrivate(QVulkanInstance *inst, VkDevice device)\n"
532"{\n"
533" QVulkanFunctions *f = inst->functions();\n"
534" Q_ASSERT(f);\n\n"
535" static constexpr auto funcNames = qOffsetStringArray(\n"
536"%s\n"
537" );\n"
538" static_assert(std::extent_v<decltype(m_funcs)> == size_t(funcNames.count()));\n"
539" for (int i = 0; i < funcNames.count(); ++i) {\n"
540" m_funcs[i] = f->vkGetDeviceProcAddr(device, funcNames.at(i));\n"
541" if (i < %d && !m_funcs[i])\n"
542" qWarning(\"QVulkanDeviceFunctions: Failed to resolve %%s\", funcNames.at(i));\n"
543" }\n"
544"}\n"
545"\n"
546"QT_END_NAMESPACE\n";
547
548 QString devCmdWrapperStr;
549 QString instCmdWrapperStr;
550 int devIdx = 0;
551 int instIdx = 0;
552 QString devCmdNamesStr;
553 QString instCmdNamesStr;
554 int vulkan10DevCount = 0;
555 int vulkan10InstCount = 0;
556
557 for (const QString &version : VERSIONS) {
558 const QStringList &coreFunctionsInVersion = versionCommandMapping[version];
559 instCmdWrapperStr += "\n#if " + version + "\n";
560 devCmdWrapperStr += "\n#if " + version + "\n";
561 for (const VkSpecParser::Command &c : commands) {
562 if (!coreFunctionsInVersion.contains(c.cmd.name))
563 continue;
564
565 QString *dst = c.deviceLevel ? &devCmdWrapperStr : &instCmdWrapperStr;
566 int *idx = c.deviceLevel ? &devIdx : &instIdx;
567 *dst += funcSig(c, c.deviceLevel ? "QVulkanDeviceFunctions" : "QVulkanFunctions");
568 *dst += QString(QStringLiteral("\n{\n Q_ASSERT(d_ptr->m_funcs[%1]);\n ")).arg(*idx);
569 *dst += funcCall(c, *idx);
570 *dst += QStringLiteral(";\n}\n\n");
571 *idx += 1;
572
573 dst = c.deviceLevel ? &devCmdNamesStr : &instCmdNamesStr;
574 *dst += QStringLiteral(" \"");
575 *dst += c.cmd.name;
576 *dst += QStringLiteral("\",\n");
577 }
578 if (version == QStringLiteral("VK_VERSION_1_0")) {
579 vulkan10InstCount = instIdx;
580 vulkan10DevCount = devIdx;
581 }
582 instCmdWrapperStr += "#endif\n\n";
583 devCmdWrapperStr += "#endif\n\n";
584 }
585
586 if (devCmdNamesStr.size() > 2)
587 devCmdNamesStr.chop(2);
588 if (instCmdNamesStr.size() > 2)
589 instCmdNamesStr.chop(2);
590
591 const QString str =
592 QString::asprintf(s, preamble.get(licHeaderFn).constData(),
593 instCmdWrapperStr.toUtf8().constData(),
594 instCmdNamesStr.toUtf8().constData(), vulkan10InstCount,
595 devCmdWrapperStr.toUtf8().constData(),
596 devCmdNamesStr.toUtf8().constData(), vulkan10DevCount);
597
598 f.write(str.toUtf8());
599
600 return true;
601}
602
603int main(int argc, char **argv)
604{
605 QCoreApplication app(argc, argv);
606 VkSpecParser parser;
607
608 if (argc < 4) {
609 qWarning("Usage: qvkgen input_vk_xml input_license_header output_base\n"
610 " For example: qvkgen vulkan/vk.xml vulkan/qvulkanfunctions.header vulkan/qvulkanfunctions");
611 return 1;
612 }
613
614 parser.setFileName(QString::fromUtf8(argv[1]));
615
616 if (!parser.parse())
617 return 1;
618
619 // Now we have a list of functions (commands), including extensions, and a
620 // table of Version (1.0, 1.1, 1.2) -> Core functions in that version.
621 QList<VkSpecParser::Command> commands = parser.commands();
622 QMap<QString, QStringList> versionCommandMapping = parser.versionCommandMapping();
623
624 QStringList ignoredFuncs {
625 QStringLiteral("vkCreateInstance"),
626 QStringLiteral("vkDestroyInstance"),
627 QStringLiteral("vkGetInstanceProcAddr"),
628 QStringLiteral("vkEnumerateInstanceVersion")
629 };
630 for (int i = 0; i < commands.size(); ++i) {
631 if (ignoredFuncs.contains(commands[i].cmd.name))
632 commands.remove(i--);
633 }
634
635 QString licenseHeaderFileName = QString::fromUtf8(argv[2]);
636 QString outputBase = QString::fromUtf8(argv[3]);
637 genVulkanFunctionsH(commands, versionCommandMapping, licenseHeaderFileName, outputBase);
638 genVulkanFunctionsPH(commands, versionCommandMapping, licenseHeaderFileName, outputBase);
639 genVulkanFunctionsPC(commands, versionCommandMapping, licenseHeaderFileName, outputBase);
640
641 return 0;
642}
QByteArray get(const QString &fn)
Definition qvkgen.cpp:306
void setFileName(const QString &fn)
Definition qvkgen.cpp:40
QList< Command > commands() const
Definition qvkgen.cpp:37
QMap< QString, QStringList > versionCommandMapping() const
Definition qvkgen.cpp:38
bool parse()
Definition qvkgen.cpp:58
static const QStringList VERSIONS
Definition qvkgen.cpp:12
QString funcCall(const VkSpecParser::Command &c, int idx)
Definition qvkgen.cpp:274
bool genVulkanFunctionsH(const QList< VkSpecParser::Command > &commands, const QMap< QString, QStringList > &versionCommandMapping, const QString &licHeaderFn, const QString &outputBase)
Definition qvkgen.cpp:324
bool genVulkanFunctionsPH(const QList< VkSpecParser::Command > &commands, const QMap< QString, QStringList > &versionCommandMapping, const QString &licHeaderFn, const QString &outputBase)
Definition qvkgen.cpp:421
bool genVulkanFunctionsPC(const QList< VkSpecParser::Command > &commands, const QMap< QString, QStringList > &versionCommandMapping, const QString &licHeaderFn, const QString &outputBase)
Definition qvkgen.cpp:498
QString funcSig(const VkSpecParser::Command &c, const char *className=nullptr)
Definition qvkgen.cpp:252
int main(int argc, char *argv[])
[ctor_close]
QList< TypedName > args
Definition qvkgen.cpp:33