summaryrefslogtreecommitdiffstats
path: root/util/glgen/codegenerator.cpp
diff options
context:
space:
mode:
authorSean Harmer <sean.harmer@kdab.com>2012-08-08 16:43:48 +0100
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-02-25 17:22:28 +0100
commit9eb9bd703b29022547a2ef4bdf2e805f0cb088dc (patch)
tree9706a13ced96962724421363bbc7c665e68a50f8 /util/glgen/codegenerator.cpp
parent3fdc4ae0ed156851c99dee4b52340cfbc0f9aaf7 (diff)
OpenGL: Add code generator for OpenGL enabler classes
This patch provides a code generator that can be executed offline to generate a few classes for enhancing Qt's support for OpenGL. The generated code effectively provides all the benefits of GLEW in its multi-context form but for all platforms and even for ES2. The code generator takes as input the official Khronos gl.spec specification and gl.tm typemap files. These provide all of the information required to create the OpenGL Desktop related classes. The classes for ES2 are hand-crafted as no similar spec files have been published. The generated code supports all OpenGL extensions listed in the Khronos registry for both Desktop OpenGL and ES 2. There is a helper factory class generated which are used by QOpenGLContext::versionFunctions(). This allows code like the following to be written: QOpenGLFunctions_3_3_Core* m_funcs = 0; m_funcs = m_context->versionFunctions<QOpenGLFunctions_3_3_Core>(); if (!m_funcs) { qWarning() << "Could not obtain required OpenGL context version"; exit(1); } if (!m_funcs->initializeOpenGLFunctions()) { qWarning() << "Failed to resolve entry points"; exit(2); } // Get an extension object QOpenGLExtension_ARB_draw_buffers* ext = 0; if (m_context->hasExtension("GL_ARB_draw_buffers")) { ext = new QOpenGLExtension_ARB_draw_buffers(); ext->initializeOpenGLFunctions(m_context); } Such usage will allow much easier and rigorous use of features in modern OpenGL and extensions both in Qt itself and by users of Qt. Follow-up patches will import the generated files and then use these to reinstate OpenGL geometry shaders, add tessellation shaders, and other OpenGL constructs. Change-Id: Id0172e8aa1fd57eb4e6979a96d10fb5a34826426 Reviewed-by: Samuel Rødal <samuel.rodal@digia.com> Reviewed-by: James Turner <james.turner@kdab.com>
Diffstat (limited to 'util/glgen/codegenerator.cpp')
-rw-r--r--util/glgen/codegenerator.cpp1099
1 files changed, 1099 insertions, 0 deletions
diff --git a/util/glgen/codegenerator.cpp b/util/glgen/codegenerator.cpp
new file mode 100644
index 0000000000..c6a77569dc
--- /dev/null
+++ b/util/glgen/codegenerator.cpp
@@ -0,0 +1,1099 @@
+/***************************************************************************
+**
+** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB)
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the utilities of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "codegenerator.h"
+
+#include <QDebug>
+#include <QFile>
+#include <QSettings>
+#include <QTextStream>
+
+static const QString extensionRegistryFileName = QStringLiteral("qopengl-extension-registry.ini");
+static const QString extensionIdGroupName = QStringLiteral("ExtensionIds");
+
+CodeGenerator::CodeGenerator()
+ : m_parser(0)
+{
+}
+
+void CodeGenerator::generateCoreClasses(const QString &baseFileName) const
+{
+ // Output header and implementation files for the backend and base class
+ writeCoreHelperClasses(baseFileName + QStringLiteral(".h"), Declaration);
+ writeCoreHelperClasses(baseFileName + QStringLiteral(".cpp"), Definition);
+
+ // Output the per-version and profile public classes
+ writeCoreClasses(baseFileName);
+
+ // We also need to generate a factory class that can be used by
+ // QOpenGLContext to actually create version function objects
+ writeCoreFactoryHeader(baseFileName + QStringLiteral("factory_p.h"));
+ writeCoreFactoryImplementation(baseFileName + QStringLiteral("factory.cpp"));
+}
+
+void CodeGenerator::generateExtensionClasses(const QString &baseFileName) const
+{
+ writeExtensionHeader(baseFileName + QStringLiteral(".h"));
+ writeExtensionImplementation(baseFileName + QStringLiteral(".cpp"));
+}
+
+bool CodeGenerator::isLegacyVersion(Version v) const
+{
+ return (v.major < 3 || (v.major == 3 && v.minor == 0));
+}
+
+bool CodeGenerator::versionHasProfiles(Version v) const
+{
+ VersionProfile vp;
+ vp.version = v;
+ return vp.hasProfiles();
+}
+
+void CodeGenerator::writeCoreHelperClasses(const QString &fileName, ClassComponent component) const
+{
+ if (!m_parser)
+ return;
+
+ QFile file(fileName);
+ if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
+ return;
+ QTextStream stream(&file);
+
+ // Write the preamble
+ writePreamble(fileName, stream);
+
+ // Iterate over each OpenGL version. For each version output a private class for
+ // core functions and a private class for deprecated functions.
+ const QString privateRootClass = QStringLiteral("QOpenGLVersionFunctionsBackend");
+ Q_FOREACH (const VersionProfile &versionProfile, m_parser->versionProfiles()) {
+ switch (component) {
+ case Declaration:
+ writeBackendClassDeclaration(stream, versionProfile, privateRootClass);
+ break;
+
+ case Definition:
+ writeBackendClassImplementation(stream, versionProfile, privateRootClass);
+ break;
+ }
+ }
+
+ // Write the postamble
+ writePostamble(fileName, stream);
+}
+
+void CodeGenerator::writeCoreClasses(const QString &baseFileName) const
+{
+ // Iterate over each OpenGL version. For each version output a public class (for legacy
+ // versions or two public classes (for modern versions with profiles). Each public class
+ // is given pointers to private classes containing the actual entry points. For example,
+ // the class for OpenGL 1.1 will have pointers to the private classes for 1.0 core, 1.1
+ // core, 1.0 deprecated and 1.1 deprecated. Whereas the class for OpenGL 3.2 Core profile
+ // will have pointers to the private classes for 1.0 core, 1.1 core, ..., 3.2 core but
+ // not to any of the deprecated private classes
+ QList<ClassComponent> components = (QList<ClassComponent>() << Declaration << Definition);
+ Q_FOREACH (const ClassComponent &component, components) {
+ const QString rootClass = QStringLiteral("QAbstractOpenGLFunctions");
+ Q_FOREACH (const Version &classVersion, m_parser->versions()) {
+ VersionProfile v;
+ v.version = classVersion;
+ v.profile = VersionProfile::CompatibilityProfile;
+
+ if (isLegacyVersion(classVersion)) {
+ switch (component) {
+ case Declaration:
+ writePublicClassDeclaration(baseFileName, v, rootClass);
+ break;
+
+ case Definition:
+ writePublicClassImplementation(baseFileName, v, rootClass);
+ break;
+ }
+ } else {
+ switch (component) {
+ case Declaration:
+ writePublicClassDeclaration(baseFileName, v, rootClass);
+ v.profile = VersionProfile::CoreProfile;
+ writePublicClassDeclaration(baseFileName, v, rootClass);
+ break;
+
+ case Definition:
+ writePublicClassImplementation(baseFileName, v, rootClass);
+ v.profile = VersionProfile::CoreProfile;
+ writePublicClassImplementation(baseFileName, v, rootClass);
+ break;
+ }
+ }
+ }
+ }
+}
+
+void CodeGenerator::writeCoreFactoryHeader(const QString &fileName) const
+{
+ if (!m_parser)
+ return;
+
+ QFile file(fileName);
+ if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
+ return;
+ QTextStream stream(&file);
+
+ // Write the preamble
+ writePreamble(fileName, stream);
+
+ // Write the postamble
+ writePostamble(fileName, stream);
+}
+
+void CodeGenerator::writeCoreFactoryImplementation(const QString &fileName) const
+{
+ if (!m_parser)
+ return;
+
+ QFile file(fileName);
+ if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
+ return;
+ QTextStream stream(&file);
+
+ // Write the preamble
+ writePreamble(fileName, stream);
+
+ // Get the set of version functions classes we need to create
+ QList<Version> versions = m_parser->versions();
+ qSort(versions.begin(), versions.end(), qGreater<Version>());
+
+ // Outout the #include statements
+ stream << QStringLiteral("#if !defined(QT_OPENGL_ES_2)") << endl;
+ Q_FOREACH (const Version &classVersion, versions) {
+ if (!versionHasProfiles(classVersion)) {
+ stream << QString(QStringLiteral("#include \"qopenglfunctions_%1_%2.h\""))
+ .arg(classVersion.major)
+ .arg(classVersion.minor) << endl;
+ } else {
+ const QList<VersionProfile::OpenGLProfile> profiles = (QList<VersionProfile::OpenGLProfile>()
+ << VersionProfile::CoreProfile << VersionProfile::CompatibilityProfile);
+
+ Q_FOREACH (const VersionProfile::OpenGLProfile profile, profiles) {
+ const QString profileSuffix = profile == VersionProfile::CoreProfile
+ ? QStringLiteral("core")
+ : QStringLiteral("compatibility");
+ stream << QString(QStringLiteral("#include \"qopenglfunctions_%1_%2_%3.h\""))
+ .arg(classVersion.major)
+ .arg(classVersion.minor)
+ .arg(profileSuffix) << endl;
+ }
+ }
+ }
+ stream << QStringLiteral("#else") << endl;
+ stream << QStringLiteral("#include \"qopenglfunctions_es2.h\"") << endl;
+ stream << QStringLiteral("#endif") << endl;
+
+ stream << endl;
+
+ stream << QStringLiteral("QT_BEGIN_NAMESPACE") << endl << endl;
+ stream << QStringLiteral("QAbstractOpenGLFunctions *QOpenGLVersionFunctionsFactory::create(const QOpenGLVersionProfile &versionProfile)") << endl;
+ stream << QStringLiteral("{") << endl;
+ stream << QStringLiteral("#if !defined(QT_OPENGL_ES_2)") << endl;
+ stream << QStringLiteral(" const int major = versionProfile.version().first;") << endl;
+ stream << QStringLiteral(" const int minor = versionProfile.version().second;") << endl << endl;
+
+ // Iterate over classes with profiles
+ stream << QStringLiteral(" if (versionProfile.hasProfiles()) {") << endl;
+ stream << QStringLiteral(" switch (versionProfile.profile()) {") << endl;
+ const QList<VersionProfile::OpenGLProfile> profiles = (QList<VersionProfile::OpenGLProfile>()
+ << VersionProfile::CoreProfile << VersionProfile::CompatibilityProfile);
+ Q_FOREACH (const VersionProfile::OpenGLProfile profile, profiles) {
+ const QString caseLabel = profile == VersionProfile::CoreProfile
+ ? QStringLiteral("QSurfaceFormat::CoreProfile")
+ : QStringLiteral("QSurfaceFormat::CompatibilityProfile");
+ stream << QString(QStringLiteral(" case %1:")).arg(caseLabel) << endl;
+
+ int i = 0;
+ Q_FOREACH (const Version &classVersion, versions) {
+ if (!versionHasProfiles(classVersion))
+ continue;
+
+ const QString ifString = (i++ == 0) ? QStringLiteral("if") : QStringLiteral("else if");
+ stream << QString(QStringLiteral(" %1 (major == %2 && minor == %3)"))
+ .arg(ifString)
+ .arg(classVersion.major)
+ .arg(classVersion.minor) << endl;
+
+ VersionProfile v;
+ v.version = classVersion;
+ v.profile = profile;
+ stream << QString(QStringLiteral(" return new %1;"))
+ .arg(generateClassName(v)) << endl;
+ }
+
+ stream << QStringLiteral(" break;") << endl << endl;
+ }
+
+ stream << QStringLiteral(" case QSurfaceFormat::NoProfile:") << endl;
+ stream << QStringLiteral(" default:") << endl;
+ stream << QStringLiteral(" break;") << endl;
+ stream << QStringLiteral(" };") << endl;
+ stream << QStringLiteral(" } else {") << endl;
+
+ // Iterate over the legacy classes (no profiles)
+ int i = 0;
+ Q_FOREACH (const Version &classVersion, versions) {
+ if (versionHasProfiles(classVersion))
+ continue;
+
+ const QString ifString = (i++ == 0) ? QStringLiteral("if") : QStringLiteral("else if");
+ stream << QString(QStringLiteral(" %1 (major == %2 && minor == %3)"))
+ .arg(ifString)
+ .arg(classVersion.major)
+ .arg(classVersion.minor) << endl;
+
+ VersionProfile v;
+ v.version = classVersion;
+ stream << QString(QStringLiteral(" return new %1;"))
+ .arg(generateClassName(v)) << endl;
+ }
+
+ stream << QStringLiteral(" }") << endl;
+ stream << QStringLiteral(" return 0;") << endl;
+
+ stream << QStringLiteral("#else") << endl;
+ stream << QStringLiteral(" Q_UNUSED(versionProfile);") << endl;
+ stream << QStringLiteral(" return new QOpenGLFunctions_ES2;") << endl;
+ stream << QStringLiteral("#endif") << endl;
+ stream << QStringLiteral("}") << endl;
+
+ // Write the postamble
+ writePostamble(fileName, stream);
+}
+
+/**
+ \returns all functions to be included in the class defined by \a classVersionProfile
+ */
+FunctionCollection CodeGenerator::functionCollection( const VersionProfile& classVersionProfile ) const
+{
+ const Version classVersion = classVersionProfile.version;
+ FunctionCollection functionSet;
+ QList<Version> versions = m_parser->versions();
+
+ // Populate these based upon the class version and profile
+ Version minVersion;
+ minVersion.major = 1;
+ minVersion.minor = 0;
+ Version maxVersion = classVersion;
+ QList<VersionProfile::OpenGLProfile> profiles;
+ profiles << VersionProfile::CoreProfile; // Always need core functions
+
+ if (isLegacyVersion(classVersion)
+ || (classVersionProfile.hasProfiles()
+ && classVersionProfile.profile == VersionProfile::CompatibilityProfile)) {
+ // For versions < 3.1 and Compatibility profile we include both core and deprecated functions
+ profiles << VersionProfile::CompatibilityProfile;
+ }
+
+ Q_FOREACH (const Version &v, versions) {
+ // Only include functions from versions in the range
+ if (v < minVersion)
+ continue;
+ if (v > maxVersion)
+ break;
+
+ Q_FOREACH (VersionProfile::OpenGLProfile profile, profiles) {
+ // Combine version and profile for this subset of functions
+ VersionProfile version;
+ version.version = v;
+ version.profile = profile;
+
+ // Fetch the functions and add to collection for this class
+ QList<Function> functions = m_parser->functionsForVersion(version);
+ functionSet.insert(version, functions);
+ }
+ }
+
+ return functionSet;
+}
+
+void CodeGenerator::writePreamble(const QString &baseFileName, QTextStream &stream, const QString replacement) const
+{
+ const QString fileName = baseFileName + QStringLiteral(".header");
+ if (!QFile::exists(fileName))
+ return;
+
+ QFile file(fileName);
+ if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ QTextStream preambleStream(&file);
+ QString preamble = preambleStream.readAll();
+ if (!replacement.isEmpty())
+ preamble.replace(QStringLiteral("__VERSION__"), replacement, Qt::CaseSensitive);
+ stream << preamble;
+ }
+}
+
+void CodeGenerator::writePostamble(const QString &baseFileName, QTextStream &stream) const
+{
+ const QString fileName = baseFileName + QStringLiteral(".footer");
+ if (!QFile::exists(fileName))
+ return;
+
+ QFile file(fileName);
+ if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ QTextStream postambleStream(&file);
+ QString postamble = postambleStream.readAll();
+ stream << postamble;
+ }
+}
+
+QString CodeGenerator::passByType(const Argument &arg) const
+{
+ QString passBy;
+ switch (arg.mode) {
+ case Argument::Reference:
+ case Argument::Array:
+ passBy = QStringLiteral("*");
+ break;
+
+ default:
+ case Argument::Value:
+ passBy = QString();
+ }
+ return passBy;
+}
+
+QString CodeGenerator::safeArgumentName(const QString& arg) const
+{
+ if (arg == QStringLiteral("near")) // MS Windows defines near and far
+ return QStringLiteral("nearVal");
+ else if (arg == QStringLiteral("far"))
+ return QStringLiteral("farVal");
+ else if (arg == QStringLiteral("d"))
+ return QStringLiteral("dd"); // Don't shadow d pointer
+ else
+ return arg;
+}
+
+QString CodeGenerator::generateClassName(const VersionProfile &classVersion, ClassVisibility visibility) const
+{
+ QString className;
+ switch ( visibility ) {
+ case Public: {
+ // Class name and base class
+ QString profileSuffix;
+ if (classVersion.hasProfiles())
+ profileSuffix = (classVersion.profile == VersionProfile::CoreProfile ? QStringLiteral("_Core") : QStringLiteral("_Compatibility"));
+
+ className = QString(QStringLiteral("QOpenGLFunctions_%1_%2%3"))
+ .arg(classVersion.version.major)
+ .arg(classVersion.version.minor)
+ .arg(profileSuffix);
+ break;
+ }
+ case Private: {
+ QString statusSuffix = (classVersion.profile == VersionProfile::CoreProfile ? QStringLiteral("_Core") : QStringLiteral("_Deprecated"));
+
+ className = QString(QStringLiteral("QOpenGLFunctions_%1_%2%3Private"))
+ .arg(classVersion.version.major)
+ .arg(classVersion.version.minor)
+ .arg(statusSuffix);
+ break;
+ }
+ }
+
+ return className;
+}
+
+void CodeGenerator::writeBackendClassDeclaration(QTextStream &stream,
+ const VersionProfile &versionProfile,
+ const QString &baseClass) const
+{
+ const QString className = backendClassName(versionProfile);
+ stream << QString(QStringLiteral("class %1 : public %2"))
+ .arg(className)
+ .arg(baseClass)
+ << endl;
+ stream << QStringLiteral("{") << endl;
+ stream << QStringLiteral("public:") << endl;
+ stream << QString( QStringLiteral(" %1(QOpenGLContext *context);") ).arg(className) << endl << endl;
+
+ // Output function used for generating key used in QOpenGLContextPrivate
+ stream << QStringLiteral(" static QOpenGLVersionStatus versionStatus();") << endl << endl;
+
+ // Get the functions needed for this class
+ FunctionList functions = m_parser->functionsForVersion(versionProfile);
+ FunctionCollection functionSet;
+ functionSet.insert(versionProfile, functions);
+
+ // Declare the functions
+ writeClassFunctionDeclarations(stream, functionSet, Private);
+
+ stream << QStringLiteral("};") << endl;
+ stream << endl;
+}
+
+void CodeGenerator::writeBackendClassImplementation(QTextStream &stream,
+ const VersionProfile &versionProfile,
+ const QString &baseClass) const
+{
+ const QString className = backendClassName(versionProfile);
+ stream << QString(QStringLiteral("%1::%1(QOpenGLContext *context)")).arg(className) << endl;
+ stream << QString(QStringLiteral(" : %1(context)")).arg(baseClass) << endl
+ << QStringLiteral("{") << endl;
+
+ // Resolve the entry points for this set of functions
+ // Get the functions needed for this class
+ FunctionList functions = m_parser->functionsForVersion(versionProfile);
+ FunctionCollection functionSet;
+ functionSet.insert(versionProfile, functions);
+ writeEntryPointResolutionCode(stream, functionSet);
+
+ stream << QStringLiteral("}") << endl << endl;
+
+ stream << QString(QStringLiteral("QOpenGLVersionStatus %1::versionStatus()")).arg(className) << endl;
+ stream << QStringLiteral("{") << endl;
+ const QString status = versionProfile.profile == VersionProfile::CoreProfile
+ ? QStringLiteral("QOpenGLVersionStatus::CoreStatus")
+ : QStringLiteral("QOpenGLVersionStatus::DeprecatedStatus");
+ stream << QString(QStringLiteral(" return QOpenGLVersionStatus(%1, %2, %3);"))
+ .arg(versionProfile.version.major)
+ .arg(versionProfile.version.minor)
+ .arg(status) << endl;
+ stream << QStringLiteral("}") << endl << endl;
+}
+
+QString CodeGenerator::coreClassFileName(const VersionProfile &versionProfile,
+ const QString& fileExtension) const
+{
+ QString profileSuffix;
+ if (versionProfile.hasProfiles())
+ profileSuffix = (versionProfile.profile == VersionProfile::CoreProfile ? QStringLiteral("_core") : QStringLiteral("_compatibility"));
+
+ const QString fileName = QString(QStringLiteral("qopenglfunctions_%1_%2%3.%4"))
+ .arg(versionProfile.version.major)
+ .arg(versionProfile.version.minor)
+ .arg(profileSuffix)
+ .arg(fileExtension);
+ return fileName;
+}
+
+void CodeGenerator::writePublicClassDeclaration(const QString &baseFileName,
+ const VersionProfile &versionProfile,
+ const QString &baseClass) const
+{
+ const QString fileName = coreClassFileName(versionProfile, QStringLiteral("h"));
+ QFile file(fileName);
+ if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
+ return;
+ QTextStream stream(&file);
+
+ // Write the preamble
+ const QString templateFileName = QString(QStringLiteral("%1__VERSION__.h"))
+ .arg(baseFileName);
+ QString profileSuffix;
+ if (versionProfile.hasProfiles())
+ profileSuffix = (versionProfile.profile == VersionProfile::CoreProfile ? QStringLiteral("_CORE") : QStringLiteral("_COMPATIBILITY"));
+
+ const QString versionProfileString = QString(QStringLiteral("_%1_%2%3"))
+ .arg(versionProfile.version.major)
+ .arg(versionProfile.version.minor)
+ .arg(profileSuffix);
+ writePreamble(templateFileName, stream, versionProfileString);
+
+ // Ctor, dtor, and initialize function;
+ const QString className = generateClassName(versionProfile, Public);
+ stream << QString(QStringLiteral("class Q_GUI_EXPORT %1 : public %2"))
+ .arg(className)
+ .arg(baseClass)
+ << endl;
+ stream << QStringLiteral("{") << endl;
+ stream << QStringLiteral("public:") << endl;
+ stream << QString(QStringLiteral(" %1();")).arg(className) << endl;
+ stream << QString(QStringLiteral(" ~%1();")).arg(className) << endl << endl;
+ stream << QStringLiteral(" bool initializeOpenGLFunctions() Q_DECL_OVERRIDE;") << endl << endl;
+
+ // Get the functions needed for this class and declare them
+ FunctionCollection functionSet = functionCollection(versionProfile);
+ writeClassFunctionDeclarations(stream, functionSet, Public);
+
+ // isCompatible function and backend variables
+ stream << QStringLiteral("private:") << endl;
+ stream << QStringLiteral(" friend class QOpenGLContext;") << endl << endl;
+ stream << QStringLiteral(" static bool isContextCompatible(QOpenGLContext *context);") << endl;
+ stream << QStringLiteral(" static QOpenGLVersionProfile versionProfile();") << endl << endl;
+ writeBackendVariableDeclarations(stream, backendsForFunctionCollection(functionSet));
+
+ stream << QStringLiteral("};") << endl << endl;
+
+ // Output the inline functions that forward OpenGL calls to the backends' entry points
+ writeClassInlineFunctions(stream, className, functionSet);
+
+ // Write the postamble
+ writePostamble(templateFileName, stream);
+}
+
+void CodeGenerator::writePublicClassImplementation(const QString &baseFileName,
+ const VersionProfile &versionProfile,
+ const QString& baseClass) const
+{
+ const QString fileName = coreClassFileName(versionProfile, QStringLiteral("cpp"));
+ QFile file(fileName);
+ if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
+ return;
+ QTextStream stream(&file);
+
+ // Write the preamble
+ const QString templateFileName = QString(QStringLiteral("%1__VERSION__.cpp"))
+ .arg(baseFileName);
+ QString profileSuffix;
+ if (versionProfile.hasProfiles())
+ profileSuffix = (versionProfile.profile == VersionProfile::CoreProfile ? QStringLiteral("_core") : QStringLiteral("_compatibility"));
+
+ const QString versionProfileString = QString(QStringLiteral("_%1_%2%3"))
+ .arg(versionProfile.version.major)
+ .arg(versionProfile.version.minor)
+ .arg(profileSuffix);
+ writePreamble(templateFileName, stream, versionProfileString);
+
+ const QString className = generateClassName(versionProfile, Public);
+ stream << QStringLiteral("/*!") << endl
+ << QStringLiteral(" \\class ") << className << endl
+ << QStringLiteral(" \\inmodule QtGui") << endl
+ << QStringLiteral(" \\since 5.1") << endl
+ << QStringLiteral(" \\brief The ") << className
+ << QStringLiteral(" class provides all functions for this version and profile of OpenGL.") << endl << endl
+ << QStringLiteral(" \\sa QAbstractOpenGLFunctions") << endl
+ << QStringLiteral("*/") << endl << endl;
+
+ // Get the data we'll need for this class implementation
+ FunctionCollection functionSet = functionCollection(versionProfile);
+ QList<VersionProfile> backends = backendsForFunctionCollection(functionSet);
+
+ // Output default constructor
+ stream << className << QStringLiteral("::") << className << QStringLiteral("()") << endl;
+ stream << QStringLiteral(" : ") << baseClass << QStringLiteral("()");
+ Q_FOREACH (const VersionProfile &v, backends)
+ stream << endl << QString(QStringLiteral(" , %1(0)")).arg(backendVariableName(v));
+ stream << endl << QStringLiteral("{") << endl << QStringLiteral("}") << endl << endl;
+
+ // Output the destructor
+ stream << className << QStringLiteral("::~") << className << QStringLiteral("()") << endl;
+ stream << QStringLiteral("{") << endl;
+ Q_FOREACH (const VersionProfile &v, backends) {
+ const QString backendVar = backendVariableName(v);
+ const QString backendClass = backendClassName(v);
+ stream << QString(QStringLiteral(" if (%1 && !%1->refs.deref()) {")).arg(backendVar) << endl;
+ stream << QString(QStringLiteral(" QAbstractOpenGLFunctionsPrivate::removeFunctionsBackend(%1->context, %2::versionStatus());"))
+ .arg(backendVar)
+ .arg(backendClass) << endl;
+ stream << QString(QStringLiteral(" delete %1;")).arg(backendVar) << endl;
+ stream << QStringLiteral(" }") << endl;
+ }
+ stream << QStringLiteral("}") << endl << endl;
+
+ // Output the initialize function that creates the backend objects
+ stream << QString(QStringLiteral("bool %1::initializeOpenGLFunctions()")).arg(className) << endl;
+ stream << QStringLiteral("{") << endl;
+
+ stream << QStringLiteral(" if ( isInitialized() )") << endl;
+ stream << QStringLiteral(" return true;") << endl << endl;
+ stream << QStringLiteral(" QOpenGLContext* context = QOpenGLContext::currentContext();") << endl << endl;
+ stream << QStringLiteral(" // If owned by a context object make sure it is current.") << endl;
+ stream << QStringLiteral(" // Also check that current context is capable of resolving all needed functions") << endl;
+ stream << QStringLiteral(" if (((owningContext() && owningContext() == context) || !owningContext())") << endl;
+ stream << QString(QStringLiteral(" && %1::isContextCompatible(context))")).arg(className) << endl;
+ stream << QStringLiteral(" {") << endl;
+ stream << QStringLiteral(" // Associate with private implementation, creating if necessary") << endl;
+ stream << QStringLiteral(" // Function pointers in the backends are resolved at creation time") << endl;
+ stream << QStringLiteral(" QOpenGLVersionFunctionsBackend* d = 0;") << endl;
+
+ Q_FOREACH (const VersionProfile &v, backends) {
+ const QString backendClass = backendClassName(v);
+ const QString backendVar = backendVariableName(v);
+ stream << QString(QStringLiteral(" d = QAbstractOpenGLFunctionsPrivate::functionsBackend(context, %1::versionStatus());"))
+ .arg(backendClass) << endl;
+ stream << QStringLiteral(" if (!d) {") << endl;
+ stream << QString(QStringLiteral(" d = new %1(context);")).arg(backendClass) << endl;
+ stream << QString(QStringLiteral(" QAbstractOpenGLFunctionsPrivate::insertFunctionsBackend(context, %1::versionStatus(), d);"))
+ .arg(backendClass) << endl;
+ stream << QStringLiteral(" }") << endl;
+ stream << QString(QStringLiteral(" %1 = static_cast<%2*>(d);")).arg(backendVar).arg(backendClass) << endl;
+ stream << QStringLiteral(" d->refs.ref();") << endl << endl;
+ }
+
+ stream << QStringLiteral(" QAbstractOpenGLFunctions::initializeOpenGLFunctions();") << endl;
+ stream << QStringLiteral(" }") << endl;
+
+ stream << QStringLiteral(" return isInitialized();") << endl;
+ stream << QStringLiteral("}") << endl << endl;
+
+ // Output the context compatibility check function
+ stream << QString(QStringLiteral("bool %1::isContextCompatible(QOpenGLContext *context)")).arg(className) << endl;
+ stream << QStringLiteral("{") << endl;
+ stream << QStringLiteral(" Q_ASSERT(context);") << endl;
+ stream << QStringLiteral(" QSurfaceFormat f = context->format();") << endl;
+ stream << QStringLiteral(" const QPair<int, int> v = qMakePair(f.majorVersion(), f.minorVersion());") << endl;
+ stream << QString(QStringLiteral(" if (v < qMakePair(%1, %2))"))
+ .arg(versionProfile.version.major)
+ .arg(versionProfile.version.minor) << endl;
+ stream << QStringLiteral(" return false;") << endl << endl;
+
+ // If generating a legacy or compatibility profile class we need to ensure that
+ // the context does not expose only core functions
+ if (versionProfile.profile != VersionProfile::CoreProfile) {
+ stream << QStringLiteral(" if (f.profile() == QSurfaceFormat::CoreProfile)") << endl;
+ stream << QStringLiteral(" return false;") << endl << endl;
+ }
+
+ stream << QStringLiteral(" return true;") << endl;
+ stream << QStringLiteral("}") << endl << endl;
+
+ // Output static function used as helper in template versionFunctions() function
+ // in QOpenGLContext
+ stream << QString(QStringLiteral("QOpenGLVersionProfile %1::versionProfile()")).arg(className) << endl;
+ stream << QStringLiteral("{") << endl;
+ stream << QStringLiteral(" QOpenGLVersionProfile v;") << endl;
+ stream << QString(QStringLiteral(" v.setVersion(%1, %2);"))
+ .arg(versionProfile.version.major)
+ .arg(versionProfile.version.minor) << endl;
+ if (versionProfile.hasProfiles()) {
+ const QString profileName = versionProfile.profile == VersionProfile::CoreProfile
+ ? QStringLiteral("QSurfaceFormat::CoreProfile")
+ : QStringLiteral("QSurfaceFormat::CompatibilityProfile");
+ stream << QString(QStringLiteral(" v.setProfile(%1);")).arg(profileName) << endl;
+ }
+ stream << QStringLiteral(" return v;") << endl;
+ stream << QStringLiteral("}") << endl;
+
+ // Write the postamble
+ writePostamble(templateFileName, stream);
+}
+
+void CodeGenerator::writeClassFunctionDeclarations(QTextStream &stream,
+ const FunctionCollection &functionSet,
+ ClassVisibility visibility) const
+{
+ Q_FOREACH (const VersionProfile &version, functionSet.keys()) {
+ // Add a comment to the header
+ stream << QString(QStringLiteral(" // OpenGL %1.%2 %3 functions"))
+ .arg(version.version.major)
+ .arg(version.version.minor)
+ .arg((version.profile == VersionProfile::CoreProfile) ? QStringLiteral("core") : QStringLiteral("deprecated"))
+ << endl;
+
+ // Output function declarations
+ FunctionList functions = functionSet.value(version);
+ Q_FOREACH (const Function &f, functions)
+ writeFunctionDeclaration(stream, f, visibility);
+ stream << endl;
+ } // version and profile
+}
+
+void CodeGenerator::writeFunctionDeclaration(QTextStream &stream, const Function &f, ClassVisibility visibility) const
+{
+ QStringList argList;
+ Q_FOREACH (const Argument &arg, f.arguments) {
+ QString a = QString(QStringLiteral("%1%2 %3%4"))
+ .arg((arg.direction == Argument::In && arg.mode != Argument::Value) ? QStringLiteral("const ") : QString())
+ .arg(arg.type)
+ .arg(passByType(arg))
+ .arg(safeArgumentName(arg.name));
+ argList.append(a);
+ }
+ QString args = argList.join(QStringLiteral(", "));
+
+ QString signature;
+ switch (visibility) {
+ case Public:
+ signature = QString(QStringLiteral(" %1 gl%2(%3);")).arg(f.returnType).arg(f.name).arg(args);
+ break;
+
+ case Private:
+ default:
+ signature = QString(QStringLiteral(" %1 (QOPENGLF_APIENTRYP %2)(%3);")).arg(f.returnType).arg(f.name).arg(args);
+ }
+ stream << signature << endl;
+}
+
+void CodeGenerator::writeClassInlineFunctions(QTextStream &stream,
+ const QString &className,
+ const FunctionCollection &functionSet) const
+{
+ Q_FOREACH (const VersionProfile &version, functionSet.keys()) {
+
+ // Add a comment to the header
+ stream << QString(QStringLiteral("// OpenGL %1.%2 %3 functions"))
+ .arg(version.version.major)
+ .arg(version.version.minor)
+ .arg((version.profile == VersionProfile::CoreProfile) ? QStringLiteral("core") : QStringLiteral("deprecated"))
+ << endl;
+
+ // Output function declarations
+ const QString backendVar = backendVariableName(version);
+ FunctionList functions = functionSet.value(version);
+ Q_FOREACH (const Function &f, functions)
+ writeInlineFunction(stream, className, backendVar, f);
+
+ stream << endl;
+
+ } // version and profile
+}
+
+void CodeGenerator::writeInlineFunction(QTextStream &stream, const QString &className,
+ const QString &backendVar, const Function &f) const
+{
+ QStringList argList;
+ Q_FOREACH (const Argument &arg, f.arguments) {
+ QString a = QString(QStringLiteral("%1%2 %3%4"))
+ .arg((arg.direction == Argument::In && arg.mode != Argument::Value) ? QStringLiteral("const ") : QString())
+ .arg(arg.type)
+ .arg(passByType(arg))
+ .arg(safeArgumentName(arg.name));
+ argList.append(a);
+ }
+ QString args = argList.join(", ");
+
+
+ QString signature = QString(QStringLiteral("inline %1 %2::gl%3(%4)"))
+ .arg(f.returnType)
+ .arg(className)
+ .arg(f.name)
+ .arg(args);
+ stream << signature << endl << QStringLiteral("{") << endl;
+
+ QStringList argumentNames;
+ Q_FOREACH (const Argument &arg, f.arguments)
+ argumentNames.append(safeArgumentName(arg.name));
+ QString argNames = argumentNames.join(", ");
+
+ if (f.returnType == QStringLiteral("void"))
+ stream << QString(QStringLiteral(" %1->%2(%3);")).arg(backendVar).arg(f.name).arg(argNames) << endl;
+ else
+ stream << QString(QStringLiteral(" return %1->%2(%3);")).arg(backendVar).arg(f.name).arg(argNames) << endl;
+ stream << QStringLiteral("}") << endl << endl;
+}
+
+void CodeGenerator::writeEntryPointResolutionCode(QTextStream &stream,
+ const FunctionCollection &functionSet) const
+{
+ bool hasModuleHandle = false;
+ Q_FOREACH (const VersionProfile &version, functionSet.keys()) {
+
+ // Add a comment to the header
+ stream << QString(QStringLiteral(" // OpenGL %1.%2 %3 functions"))
+ .arg(version.version.major)
+ .arg(version.version.minor)
+ .arg((version.profile == VersionProfile::CoreProfile) ? QStringLiteral("core") : QStringLiteral("deprecated"))
+ << endl;
+
+ // Output function declarations
+ FunctionList functions = functionSet.value(version);
+
+ bool useGetProcAddress = (version.version.major == 1 && (version.version.minor == 0 || version.version.minor == 1));
+ if (useGetProcAddress) {
+ stream << "#if defined(Q_OS_WIN)" << endl;
+ if (!hasModuleHandle) {
+ stream << " HMODULE handle = GetModuleHandleA(\"opengl32.dll\");" << endl;
+ hasModuleHandle = true;
+ }
+
+ Q_FOREACH (const Function &f, functions)
+ writeEntryPointResolutionStatement(stream, f, QString(), useGetProcAddress);
+
+ stream << "#else" << endl;
+ }
+
+ Q_FOREACH (const Function &f, functions)
+ writeEntryPointResolutionStatement(stream, f);
+
+ if (useGetProcAddress)
+ stream << "#endif" << endl;
+
+ stream << endl;
+
+ } // version and profile
+}
+
+void CodeGenerator::writeEntryPointResolutionStatement(QTextStream &stream, const Function &f,
+ const QString &prefix, bool useGetProcAddress) const
+{
+ QStringList argList;
+ Q_FOREACH (const Argument &arg, f.arguments) {
+ QString a = QString("%1%2 %3")
+ .arg((arg.direction == Argument::In && arg.mode != Argument::Value) ? QStringLiteral("const ") : QString())
+ .arg(arg.type)
+ .arg(passByType(arg));
+ argList.append(a);
+ }
+ QString args = argList.join(QStringLiteral(", "));
+
+ QString signature;
+ if (!useGetProcAddress) {
+ signature = QString(QStringLiteral(" %4%3 = reinterpret_cast<%1 (QOPENGLF_APIENTRYP)(%2)>(context->getProcAddress(\"gl%3\"));"))
+ .arg(f.returnType)
+ .arg(args)
+ .arg(f.name)
+ .arg(prefix);
+ } else {
+ signature = QString(QStringLiteral(" %4%3 = reinterpret_cast<%1 (QOPENGLF_APIENTRYP)(%2)>(GetProcAddress(handle, \"gl%3\"));"))
+ .arg(f.returnType)
+ .arg(args)
+ .arg(f.name)
+ .arg(prefix);
+ }
+ stream << signature << endl;
+}
+
+QList<VersionProfile> CodeGenerator::backendsForFunctionCollection(const FunctionCollection &functionSet) const
+{
+ QList<VersionProfile> backends;
+ Q_FOREACH (const VersionProfile &versionProfile, functionSet.keys()) {
+ if (m_parser->versionProfiles().contains(versionProfile))
+ backends.append(versionProfile);
+ }
+ return backends;
+}
+
+QString CodeGenerator::backendClassName(const VersionProfile &v) const
+{
+ QString statusSuffix = v.profile == VersionProfile::CoreProfile
+ ? QStringLiteral("_Core")
+ : QStringLiteral("_Deprecated");
+ const QString className = QString(QStringLiteral("QOpenGLFunctions_%1_%2%3Backend"))
+ .arg(v.version.major)
+ .arg(v.version.minor)
+ .arg(statusSuffix);
+ return className;
+}
+
+QString CodeGenerator::backendVariableName(const VersionProfile &v) const
+{
+ const QString status = (v.profile == VersionProfile::CoreProfile)
+ ? QStringLiteral("Core")
+ : QStringLiteral("Deprecated");
+ const QString varName = QString(QStringLiteral("d_%1_%2_%3"))
+ .arg(v.version.major)
+ .arg(v.version.minor)
+ .arg(status);
+ return varName;
+}
+
+void CodeGenerator::writeBackendVariableDeclarations(QTextStream &stream, const QList<VersionProfile> &backends) const
+{
+ // We need a private class for each version and profile (status: core or deprecated)
+ Q_FOREACH (const VersionProfile &v, backends) {
+ const QString className = backendClassName(v);
+ const QString varName = backendVariableName(v);
+ stream << QString(QStringLiteral(" %1* %2;")).arg(className).arg(varName) << endl;
+ }
+}
+
+void CodeGenerator::writeExtensionHeader(const QString &fileName) const
+{
+ if (!m_parser)
+ return;
+
+ QFile file(fileName);
+ if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
+ return;
+ QTextStream stream(&file);
+
+ // Write the preamble
+ writePreamble(fileName, stream);
+
+ // Iterate through the list of extensions and create one class per extension
+ QStringList extensions = m_parser->extensions();
+ Q_FOREACH (const QString &extension, extensions) {
+ writeExtensionClassDeclaration(stream, extension, Private);
+ writeExtensionClassDeclaration(stream, extension, Public);
+ }
+
+ // Write the postamble
+ writePostamble(fileName, stream);
+}
+
+void CodeGenerator::writeExtensionImplementation(const QString &fileName) const
+{
+ if (!m_parser)
+ return;
+
+ QFile file(fileName);
+ if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
+ return;
+ QTextStream stream(&file);
+
+ // Write the preamble
+ writePreamble(fileName, stream);
+
+ // Iterate through the list of extensions and create one class per extension
+ QStringList extensions = m_parser->extensions();
+ Q_FOREACH (const QString &extension, extensions)
+ writeExtensionClassImplementation(stream, extension);
+
+ // Write the postamble
+ writePostamble(fileName, stream);
+}
+
+void CodeGenerator::writeExtensionClassDeclaration(QTextStream &stream, const QString &extension, ClassVisibility visibility) const
+{
+ const QString className = generateExtensionClassName(extension, visibility);
+
+ QString baseClass = (visibility == Public) ? QStringLiteral("QAbstractOpenGLExtension") : QStringLiteral("QAbstractOpenGLExtensionPrivate");
+
+ stream << QString(QStringLiteral("class %2 : public %3"))
+ .arg(className)
+ .arg(baseClass)
+ << endl << "{" << endl << "public:" << endl;
+
+ if (visibility == Public) {
+ // Default constructor
+ stream << QStringLiteral(" ") << className << QStringLiteral("();") << endl << endl;
+
+ // Base class virtual function(s)
+ QString resolveFunction = QStringLiteral(" bool initializeOpenGLFunctions() Q_DECL_FINAL;");
+ stream << resolveFunction << endl << endl;
+ }
+
+ // Output the functions provided by this extension
+ QList<Function> functions = m_parser->functionsForExtension(extension);
+ Q_FOREACH (const Function &f, functions)
+ writeFunctionDeclaration(stream, f, visibility);
+
+ if (visibility == Public) {
+ // Write out the protected ctor
+ stream << endl << QStringLiteral("protected:") << endl;
+ stream << QStringLiteral(" Q_DECLARE_PRIVATE(") << className << QStringLiteral(")") << endl;
+ }
+
+ // End the class declaration
+ stream << QStringLiteral("};") << endl << endl;
+
+ // Output the inline functions for public class
+ if (visibility == Public) {
+ Q_FOREACH (const Function &f, functions)
+ writeExtensionInlineFunction(stream, className, f);
+ }
+}
+
+void CodeGenerator::writeExtensionInlineFunction(QTextStream &stream, const QString &className, const Function &f) const
+{
+ QStringList argList;
+ Q_FOREACH (const Argument &arg, f.arguments) {
+ QString a = QString(QStringLiteral("%1%2 %3%4"))
+ .arg((arg.direction == Argument::In && arg.mode != Argument::Value) ? QStringLiteral("const ") : QString())
+ .arg(arg.type)
+ .arg(passByType(arg))
+ .arg(safeArgumentName(arg.name));
+ argList.append(a);
+ }
+ QString args = argList.join(", ");
+
+
+ QString signature = QString(QStringLiteral("inline %1 %2::gl%3(%4)"))
+ .arg(f.returnType)
+ .arg(className)
+ .arg(f.name)
+ .arg(args);
+ stream << signature << endl << QStringLiteral("{") << endl;
+
+ stream << QString(QStringLiteral(" Q_D(%1);")).arg(className) << endl;
+
+ QStringList argumentNames;
+ Q_FOREACH (const Argument &arg, f.arguments)
+ argumentNames.append(safeArgumentName(arg.name));
+ QString argNames = argumentNames.join(", ");
+
+ if (f.returnType == QStringLiteral("void"))
+ stream << QString(QStringLiteral(" d->%1(%2);")).arg(f.name).arg(argNames) << endl;
+ else
+ stream << QString(QStringLiteral(" return d->%1(%2);")).arg(f.name).arg(argNames) << endl;
+ stream << QStringLiteral("}") << endl << endl;
+}
+
+void CodeGenerator::writeExtensionClassImplementation(QTextStream &stream, const QString &extension) const
+{
+ const QString className = generateExtensionClassName(extension);
+ const QString privateClassName = generateExtensionClassName(extension, Private);
+
+ // Output default constructor
+ stream << className << QStringLiteral("::") << className << QStringLiteral("()") << endl;
+ stream << QStringLiteral(" : QAbstractOpenGLExtension(*(new ") << privateClassName << QStringLiteral("))") << endl;
+ stream << QStringLiteral("{") << endl << QStringLiteral("}") << endl << endl;
+
+
+ // Output function to initialize this class
+ stream << QStringLiteral("bool ") << className
+ << QStringLiteral("::initializeOpenGLFunctions()") << endl
+ << QStringLiteral("{") << endl;
+
+ stream << QStringLiteral(" if (isInitialized())") << endl;
+ stream << QStringLiteral(" return true;") << endl << endl;
+
+ stream << QStringLiteral(" QOpenGLContext *context = QOpenGLContext::currentContext();") << endl;
+ stream << QStringLiteral(" if (!context) {") << endl;
+ stream << QStringLiteral(" qWarning(\"A current OpenGL context is required to resolve OpenGL extension functions\");")
+ << endl;
+ stream << QStringLiteral(" return false;") << endl;
+ stream << QStringLiteral(" }") << endl << endl;
+
+ // Output code to resolve entry points for this class
+ stream << QStringLiteral(" // Resolve the functions") << endl;
+ stream << QStringLiteral(" Q_D(") << className << QStringLiteral(");") << endl;
+ stream << endl;
+
+ // Output function declarations
+ QList<Function> functions = m_parser->functionsForExtension(extension);
+ Q_FOREACH (const Function &f, functions)
+ writeEntryPointResolutionStatement(stream, f, QStringLiteral("d->"));
+
+ // Call the base class implementation
+ stream << QStringLiteral(" QAbstractOpenGLExtension::initializeOpenGLFunctions();") << endl;
+
+ // Finish off
+ stream << QStringLiteral(" return true;") << endl;
+ stream << QStringLiteral("}") << endl << endl;
+}
+
+QString CodeGenerator::generateExtensionClassName(const QString &extension, ClassVisibility visibility) const
+{
+ QString visibilitySuffix;
+ if (visibility == Private)
+ visibilitySuffix = QStringLiteral("Private");
+
+ return QString(QStringLiteral("QOpenGLExtension_%1%2"))
+ .arg(extension)
+ .arg(visibilitySuffix);
+}