qtbase-opensource-src/util/glgen/codegenerator.cpp

1106 lines
47 KiB
C++

/***************************************************************************
**
** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB)
** Contact: https://www.qt.io/licensing/
**
** This file is part of the utilities 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 "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();
std::sort(versions.begin(), versions.end(), std::greater<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 == QLatin1String("near")) // MS Windows defines near and far
return QStringLiteral("nearVal");
else if (arg == QLatin1String("far"))
return QStringLiteral("farVal");
else if (arg == QLatin1String("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() 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(" \\wrapper") << endl
<< QStringLiteral(" \\brief The ") << className
<< QString(QStringLiteral(" class provides all functions for OpenGL %1.%2 "))
.arg(versionProfile.version.major)
.arg(versionProfile.version.minor);
if (!profileSuffix.isEmpty()) {
profileSuffix.remove(0, 1);
profileSuffix.append(QStringLiteral(" profile"));
} else {
profileSuffix = "specification";
}
stream << profileSuffix << QStringLiteral(".") << endl << endl
<< QStringLiteral(" This class is a wrapper for functions from ")
<< QString(QStringLiteral("OpenGL %1.%2 "))
.arg(versionProfile.version.major)
.arg(versionProfile.version.minor)
<< profileSuffix << QStringLiteral(".") << endl
<< QStringLiteral(" See reference pages on \\l {http://www.opengl.org/sdk/docs/}{opengl.org}") << endl
<< QStringLiteral(" for function documentation.") << 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 == QLatin1String("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() 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);
}