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