summaryrefslogtreecommitdiffstats
path: root/src/tools/qvkgen/qvkgen.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/qvkgen/qvkgen.cpp')
-rw-r--r--src/tools/qvkgen/qvkgen.cpp530
1 files changed, 530 insertions, 0 deletions
diff --git a/src/tools/qvkgen/qvkgen.cpp b/src/tools/qvkgen/qvkgen.cpp
new file mode 100644
index 0000000000..059f9413cb
--- /dev/null
+++ b/src/tools/qvkgen/qvkgen.cpp
@@ -0,0 +1,530 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** 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-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qvector.h>
+#include <QtCore/qfile.h>
+#include <QtCore/qfileinfo.h>
+#include <QtCore/qxmlstream.h>
+
+class VkSpecParser
+{
+public:
+ bool parse();
+
+ struct TypedName {
+ QString name;
+ QString type;
+ QString typeSuffix;
+ };
+
+ struct Command {
+ TypedName cmd;
+ QVector<TypedName> args;
+ bool deviceLevel;
+ };
+
+ QVector<Command> commands() const { return m_commands; }
+
+ void setFileName(const QString &fn) { m_fn = fn; }
+
+private:
+ void skip();
+ void parseCommands();
+ Command parseCommand();
+ TypedName parseParamOrProto(const QString &tag);
+ QString parseName();
+
+ QFile m_file;
+ QXmlStreamReader m_reader;
+ QVector<Command> m_commands;
+ QString m_fn;
+};
+
+bool VkSpecParser::parse()
+{
+ m_file.setFileName(m_fn);
+ if (!m_file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ qWarning("Failed to open %s", qPrintable(m_file.fileName()));
+ return false;
+ }
+
+ m_reader.setDevice(&m_file);
+ while (!m_reader.atEnd()) {
+ m_reader.readNext();
+ if (m_reader.isStartElement()) {
+ if (m_reader.name() == QStringLiteral("commands"))
+ parseCommands();
+ }
+ }
+
+ return true;
+}
+
+void VkSpecParser::skip()
+{
+ QString tag = m_reader.name().toString();
+ while (!m_reader.atEnd()) {
+ m_reader.readNext();
+ if (m_reader.isEndElement() && m_reader.name() == tag)
+ break;
+ }
+}
+
+void VkSpecParser::parseCommands()
+{
+ m_commands.clear();
+
+ while (!m_reader.atEnd()) {
+ m_reader.readNext();
+ if (m_reader.isEndElement() && m_reader.name() == QStringLiteral("commands"))
+ return;
+ if (m_reader.isStartElement() && m_reader.name() == "command")
+ m_commands.append(parseCommand());
+ }
+}
+
+VkSpecParser::Command VkSpecParser::parseCommand()
+{
+ Command c;
+
+ while (!m_reader.atEnd()) {
+ m_reader.readNext();
+ if (m_reader.isEndElement() && m_reader.name() == QStringLiteral("command"))
+ break;
+ if (m_reader.isStartElement()) {
+ const QString protoStr = QStringLiteral("proto");
+ const QString paramStr = QStringLiteral("param");
+ if (m_reader.name() == protoStr) {
+ c.cmd = parseParamOrProto(protoStr);
+ } else if (m_reader.name() == paramStr) {
+ c.args.append(parseParamOrProto(paramStr));
+ } else {
+ skip();
+ }
+ }
+ }
+
+ c.deviceLevel = false;
+ if (!c.args.isEmpty()) {
+ QStringList dispatchableDeviceAndChildTypes {
+ QStringLiteral("VkDevice"),
+ QStringLiteral("VkQueue"),
+ QStringLiteral("VkCommandBuffer")
+ };
+ if (dispatchableDeviceAndChildTypes.contains(c.args[0].type)
+ && c.cmd.name != QStringLiteral("vkGetDeviceProcAddr"))
+ {
+ c.deviceLevel = true;
+ }
+ }
+
+ return c;
+}
+
+VkSpecParser::TypedName VkSpecParser::parseParamOrProto(const QString &tag)
+{
+ TypedName t;
+
+ while (!m_reader.atEnd()) {
+ m_reader.readNext();
+ if (m_reader.isEndElement() && m_reader.name() == tag)
+ break;
+ if (m_reader.isStartElement()) {
+ if (m_reader.name() == QStringLiteral("name")) {
+ t.name = parseName();
+ } else if (m_reader.name() != QStringLiteral("type")) {
+ skip();
+ }
+ } else {
+ QStringRef text = m_reader.text().trimmed();
+ if (!text.isEmpty()) {
+ if (text.startsWith(QLatin1Char('['))) {
+ t.typeSuffix += text;
+ } else {
+ if (!t.type.isEmpty())
+ t.type += QLatin1Char(' ');
+ t.type += text;
+ }
+ }
+ }
+ }
+
+ return t;
+}
+
+QString VkSpecParser::parseName()
+{
+ QString name;
+ while (!m_reader.atEnd()) {
+ m_reader.readNext();
+ if (m_reader.isEndElement() && m_reader.name() == QStringLiteral("name"))
+ break;
+ name += m_reader.text();
+ }
+ return name.trimmed();
+}
+
+QString funcSig(const VkSpecParser::Command &c, const char *className = nullptr)
+{
+ QString s;
+ s.sprintf("%s %s%s%s", qPrintable(c.cmd.type),
+ (className ? className : ""), (className ? "::" : ""),
+ qPrintable(c.cmd.name));
+ if (!c.args.isEmpty()) {
+ s += QLatin1Char('(');
+ bool first = true;
+ for (const VkSpecParser::TypedName &a : c.args) {
+ QString argStr;
+ argStr.sprintf("%s%s%s%s", qPrintable(a.type), (a.type.endsWith(QLatin1Char('*')) ? "" : " "),
+ qPrintable(a.name), qPrintable(a.typeSuffix));
+ if (!first)
+ s += QStringLiteral(", ");
+ else
+ first = false;
+ s += argStr;
+ }
+ s += QLatin1Char(')');
+ }
+ return s;
+}
+
+QString funcCall(const VkSpecParser::Command &c, int idx)
+{
+ QString s;
+ // template:
+ // [return] reinterpret_cast<PFN_vkEnumeratePhysicalDevices>(d_ptr->m_funcs[0])(instance, pPhysicalDeviceCount, pPhysicalDevices);
+ s.sprintf("%sreinterpret_cast<PFN_%s>(d_ptr->m_funcs[%d])",
+ (c.cmd.type == QStringLiteral("void") ? "" : "return "),
+ qPrintable(c.cmd.name),
+ idx);
+ if (!c.args.isEmpty()) {
+ s += QLatin1Char('(');
+ bool first = true;
+ for (const VkSpecParser::TypedName &a : c.args) {
+ if (!first)
+ s += QStringLiteral(", ");
+ else
+ first = false;
+ s += a.name;
+ }
+ s += QLatin1Char(')');
+ }
+ return s;
+}
+
+class Preamble
+{
+public:
+ QByteArray get(const QString &fn);
+
+private:
+ QByteArray m_str;
+} preamble;
+
+QByteArray Preamble::get(const QString &fn)
+{
+ if (!m_str.isEmpty())
+ return m_str;
+
+ QFile f(fn);
+ if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ qWarning("Failed to open %s", qPrintable(fn));
+ return m_str;
+ }
+
+ m_str = f.readAll();
+ m_str.replace("FOO", "QtGui");
+ m_str += "\n// This file is automatically generated by qvkgen. Do not edit.\n";
+
+ return m_str;
+}
+
+bool genVulkanFunctionsH(const QVector<VkSpecParser::Command> &commands, const QString &licHeaderFn, const QString &outputBase)
+{
+ QFile f(outputBase + QStringLiteral(".h"));
+ if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) {
+ qWarning("Failed to write %s", qPrintable(f.fileName()));
+ return false;
+ }
+
+ static const char *s =
+"%s\n"
+"#ifndef QVULKANFUNCTIONS_H\n"
+"#define QVULKANFUNCTIONS_H\n"
+"\n"
+"#include <QtGui/qtguiglobal.h>\n"
+"\n"
+"#if QT_CONFIG(vulkan) || defined(Q_CLANG_QDOC)\n"
+"\n"
+"#ifndef VK_NO_PROTOTYPES\n"
+"#define VK_NO_PROTOTYPES\n"
+"#endif\n"
+"#include <vulkan/vulkan.h>\n"
+"\n"
+"#include <QtCore/qscopedpointer.h>\n"
+"\n"
+"QT_BEGIN_NAMESPACE\n"
+"\n"
+"class QVulkanInstance;\n"
+"class QVulkanFunctionsPrivate;\n"
+"class QVulkanDeviceFunctionsPrivate;\n"
+"\n"
+"class Q_GUI_EXPORT QVulkanFunctions\n"
+"{\n"
+"public:\n"
+" ~QVulkanFunctions();\n"
+"\n"
+"%s\n"
+"private:\n"
+" Q_DISABLE_COPY(QVulkanFunctions)\n"
+" QVulkanFunctions(QVulkanInstance *inst);\n"
+"\n"
+" QScopedPointer<QVulkanFunctionsPrivate> d_ptr;\n"
+" friend class QVulkanInstance;\n"
+"};\n"
+"\n"
+"class Q_GUI_EXPORT QVulkanDeviceFunctions\n"
+"{\n"
+"public:\n"
+" ~QVulkanDeviceFunctions();\n"
+"\n"
+"%s\n"
+"private:\n"
+" Q_DISABLE_COPY(QVulkanDeviceFunctions)\n"
+" QVulkanDeviceFunctions(QVulkanInstance *inst, VkDevice device);\n"
+"\n"
+" QScopedPointer<QVulkanDeviceFunctionsPrivate> d_ptr;\n"
+" friend class QVulkanInstance;\n"
+"};\n"
+"\n"
+"QT_END_NAMESPACE\n"
+"\n"
+"#endif // QT_CONFIG(vulkan) || defined(Q_CLANG_QDOC)\n"
+"\n"
+"#endif // QVULKANFUNCTIONS_H\n";
+
+ QString instCmdStr;
+ QString devCmdStr;
+ for (const VkSpecParser::Command &c : commands) {
+ QString *dst = c.deviceLevel ? &devCmdStr : &instCmdStr;
+ *dst += QStringLiteral(" ");
+ *dst += funcSig(c);
+ *dst += QStringLiteral(";\n");
+ }
+
+ QString str;
+ str.sprintf(s, preamble.get(licHeaderFn).constData(), instCmdStr.toUtf8().constData(), devCmdStr.toUtf8().constData());
+
+ f.write(str.toUtf8());
+
+ return true;
+}
+
+bool genVulkanFunctionsPH(const QVector<VkSpecParser::Command> &commands, const QString &licHeaderFn, const QString &outputBase)
+{
+ QFile f(outputBase + QStringLiteral("_p.h"));
+ if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) {
+ qWarning("Failed to write %s", qPrintable(f.fileName()));
+ return false;
+ }
+
+ static const char *s =
+"%s\n"
+"#ifndef QVULKANFUNCTIONS_P_H\n"
+"#define QVULKANFUNCTIONS_P_H\n"
+"\n"
+"//\n"
+"// W A R N I N G\n"
+"// -------------\n"
+"//\n"
+"// This file is not part of the Qt API. It exists purely as an\n"
+"// implementation detail. This header file may change from version to\n"
+"// version without notice, or even be removed.\n"
+"//\n"
+"// We mean it.\n"
+"//\n"
+"\n"
+"#include \"qvulkanfunctions.h\"\n"
+"\n"
+"QT_BEGIN_NAMESPACE\n"
+"\n"
+"class QVulkanInstance;\n"
+"\n"
+"class QVulkanFunctionsPrivate\n"
+"{\n"
+"public:\n"
+" QVulkanFunctionsPrivate(QVulkanInstance *inst);\n"
+"\n"
+" PFN_vkVoidFunction m_funcs[%d];\n"
+"};\n"
+"\n"
+"class QVulkanDeviceFunctionsPrivate\n"
+"{\n"
+"public:\n"
+" QVulkanDeviceFunctionsPrivate(QVulkanInstance *inst, VkDevice device);\n"
+"\n"
+" PFN_vkVoidFunction m_funcs[%d];\n"
+"};\n"
+"\n"
+"QT_END_NAMESPACE\n"
+"\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;
+
+ QString str;
+ str.sprintf(s, preamble.get(licHeaderFn).constData(), instLevelCount, devLevelCount);
+
+ f.write(str.toUtf8());
+
+ return true;
+}
+
+bool genVulkanFunctionsPC(const QVector<VkSpecParser::Command> &commands, const QString &licHeaderFn, const QString &outputBase)
+{
+ QFile f(outputBase + QStringLiteral("_p.cpp"));
+ if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) {
+ qWarning("Failed to write %s", qPrintable(f.fileName()));
+ return false;
+ }
+
+ static const char *s =
+"%s\n"
+"#include \"qvulkanfunctions_p.h\"\n"
+"#include \"qvulkaninstance.h\"\n"
+"\n"
+"QT_BEGIN_NAMESPACE\n"
+"\n%s"
+"QVulkanFunctionsPrivate::QVulkanFunctionsPrivate(QVulkanInstance *inst)\n"
+"{\n"
+" static const char *funcNames[] = {\n"
+"%s\n"
+" };\n"
+" for (int i = 0; i < %d; ++i) {\n"
+" m_funcs[i] = inst->getInstanceProcAddr(funcNames[i]);\n"
+" if (!m_funcs[i])\n"
+" qWarning(\"QVulkanFunctions: Failed to resolve %%s\", funcNames[i]);\n"
+" }\n"
+"}\n"
+"\n%s"
+"QVulkanDeviceFunctionsPrivate::QVulkanDeviceFunctionsPrivate(QVulkanInstance *inst, VkDevice device)\n"
+"{\n"
+" QVulkanFunctions *f = inst->functions();\n"
+" Q_ASSERT(f);\n\n"
+" static const char *funcNames[] = {\n"
+"%s\n"
+" };\n"
+" for (int i = 0; i < %d; ++i) {\n"
+" m_funcs[i] = f->vkGetDeviceProcAddr(device, funcNames[i]);\n"
+" if (!m_funcs[i])\n"
+" qWarning(\"QVulkanDeviceFunctions: Failed to resolve %%s\", funcNames[i]);\n"
+" }\n"
+"}\n"
+"\n"
+"QT_END_NAMESPACE\n";
+
+ QString devCmdWrapperStr;
+ QString instCmdWrapperStr;
+ int devIdx = 0;
+ int instIdx = 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");
+ }
+
+ if (devCmdNamesStr.count() > 2)
+ devCmdNamesStr = devCmdNamesStr.left(devCmdNamesStr.count() - 2);
+ if (instCmdNamesStr.count() > 2)
+ instCmdNamesStr = instCmdNamesStr.left(instCmdNamesStr.count() - 2);
+
+ QString str;
+ str.sprintf(s, preamble.get(licHeaderFn).constData(),
+ instCmdWrapperStr.toUtf8().constData(), instCmdNamesStr.toUtf8().constData(), instIdx,
+ devCmdWrapperStr.toUtf8().constData(), devCmdNamesStr.toUtf8().constData(), commands.count() - instIdx);
+
+ f.write(str.toUtf8());
+
+ return true;
+}
+
+int main(int argc, char **argv)
+{
+ QCoreApplication app(argc, argv);
+ VkSpecParser parser;
+
+ if (argc < 4) {
+ qWarning("Usage: qvkgen input_vk_xml input_license_header output_base\n"
+ " For example: qvkgen vulkan/vk.xml vulkan/qvulkanfunctions.header vulkan/qvulkanfunctions");
+ return 1;
+ }
+
+ parser.setFileName(QString::fromUtf8(argv[1]));
+
+ if (!parser.parse())
+ return 1;
+
+ QVector<VkSpecParser::Command> commands = parser.commands();
+ QStringList ignoredFuncs {
+ QStringLiteral("vkCreateInstance"),
+ QStringLiteral("vkDestroyInstance"),
+ QStringLiteral("vkGetInstanceProcAddr")
+ };
+
+ // 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))
+ 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);
+
+ return 0;
+}