2022-05-10 10:06:48 +00:00
|
|
|
// Copyright (C) 2016 The Qt Company Ltd.
|
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
2015-01-27 13:40:04 +00:00
|
|
|
|
|
|
|
#include <QtGui/QOpenGLFunctions>
|
|
|
|
#include <QtGui/QScreen>
|
|
|
|
#include <QtGui/QWindow>
|
|
|
|
#include <private/qguiapplication_p.h>
|
|
|
|
#include <qpa/qplatformintegration.h>
|
|
|
|
#include <qpa/qplatformnativeinterface.h>
|
2015-02-05 14:45:53 +00:00
|
|
|
#include <private/qopengl_p.h>
|
2015-01-27 13:40:04 +00:00
|
|
|
|
2020-11-26 16:31:50 +00:00
|
|
|
#include <QTest>
|
2015-01-27 13:40:04 +00:00
|
|
|
|
|
|
|
#include <QtCore/QSysInfo>
|
|
|
|
#include <QtCore/QLibraryInfo>
|
|
|
|
#include <QtCore/QScopedPointer>
|
|
|
|
#include <QtCore/QVariant>
|
|
|
|
#include <QtCore/QDebug>
|
|
|
|
#include <QtCore/QTextStream>
|
2015-08-07 07:42:41 +00:00
|
|
|
#include <QtCore/QJsonDocument>
|
2015-01-27 13:40:04 +00:00
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
|
|
|
|
#define DUMP_CAPABILITY(str, integration, capability) \
|
|
|
|
if (platformIntegration->hasCapability(QPlatformIntegration::capability)) \
|
|
|
|
str << ' ' << #capability;
|
|
|
|
|
|
|
|
QTextStream &operator<<(QTextStream &str, const QSize &s)
|
|
|
|
{
|
|
|
|
str << s.width() << 'x' << s.height();
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
QTextStream &operator<<(QTextStream &str, const QRect &r)
|
|
|
|
{
|
|
|
|
str << r.size() << '+' << r.x() << '+' << r.y();
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
QTextStream &operator<<(QTextStream &str, const QSizeF &s)
|
|
|
|
{
|
|
|
|
str << s.width() << 'x' << s.height();
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
QTextStream &operator<<(QTextStream &str, const QSurfaceFormat &format)
|
|
|
|
{
|
|
|
|
str << "Version: " << format.majorVersion() << '.'
|
|
|
|
<< format.minorVersion() << " Profile: " << format.profile()
|
|
|
|
<< " Swap behavior: " << format.swapBehavior()
|
|
|
|
<< " Buffer size (RGB";
|
|
|
|
if (format.hasAlpha())
|
|
|
|
str << 'A';
|
|
|
|
str << "): " << format.redBufferSize() << ',' << format.greenBufferSize()
|
|
|
|
<< ',' << format.blueBufferSize();
|
|
|
|
if (format.hasAlpha())
|
|
|
|
str << ',' << format.alphaBufferSize();
|
|
|
|
if (const int dbs = format.depthBufferSize())
|
|
|
|
str << " Depth buffer: " << dbs;
|
|
|
|
if (const int sbs = format.stencilBufferSize())
|
|
|
|
str << " Stencil buffer: " << sbs;
|
|
|
|
const int samples = format.samples();
|
|
|
|
if (samples > 0)
|
|
|
|
str << " Samples: " << samples;
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This test contains code from the qtdiag tool. Its purpose is to output the
|
|
|
|
* graphics configuration to the CI log and to verify that Open GL can be
|
|
|
|
* initialized for platforms on which the qopengl test is marked as
|
|
|
|
* insignificant. */
|
|
|
|
|
|
|
|
class tst_QOpenGlConfig : public QObject
|
|
|
|
{
|
|
|
|
Q_OBJECT
|
|
|
|
|
|
|
|
private slots:
|
2017-04-07 06:42:22 +00:00
|
|
|
void initTestCase();
|
2015-01-27 13:40:04 +00:00
|
|
|
void testConfiguration();
|
|
|
|
void testGlConfiguration();
|
2015-02-05 14:45:53 +00:00
|
|
|
void testBugList();
|
2015-08-07 07:42:41 +00:00
|
|
|
void testDefaultWindowsBlacklist();
|
2015-01-27 13:40:04 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static void dumpConfiguration(QTextStream &str)
|
|
|
|
{
|
|
|
|
const QPlatformIntegration *platformIntegration = QGuiApplicationPrivate::platformIntegration();
|
|
|
|
str << "\nBuild : " << QLibraryInfo::build()
|
|
|
|
<< "\nPlatform : " << QGuiApplication::platformName()
|
|
|
|
<< "\nOS : " << QSysInfo::prettyProductName() << " ["
|
|
|
|
<< QSysInfo::kernelType() << " version " << QSysInfo::kernelVersion() << ']'
|
|
|
|
<< "\nArchitecture : " << QSysInfo::currentCpuArchitecture()
|
|
|
|
<< "\nCapabilities :";
|
|
|
|
DUMP_CAPABILITY(str, platformIntegration, ThreadedPixmaps)
|
|
|
|
DUMP_CAPABILITY(str, platformIntegration, OpenGL)
|
|
|
|
DUMP_CAPABILITY(str, platformIntegration, ThreadedOpenGL)
|
|
|
|
DUMP_CAPABILITY(str, platformIntegration, SharedGraphicsCache)
|
|
|
|
DUMP_CAPABILITY(str, platformIntegration, BufferQueueingOpenGL)
|
|
|
|
DUMP_CAPABILITY(str, platformIntegration, WindowMasks)
|
|
|
|
DUMP_CAPABILITY(str, platformIntegration, RasterGLSurface)
|
|
|
|
DUMP_CAPABILITY(str, platformIntegration, AllGLFunctionsQueryable)
|
|
|
|
str << '\n';
|
|
|
|
|
|
|
|
const QList<QScreen*> screens = QGuiApplication::screens();
|
|
|
|
const int screenCount = screens.size();
|
|
|
|
str << "\nScreens: " << screenCount << '\n';
|
|
|
|
for (int s = 0; s < screenCount; ++s) {
|
|
|
|
const QScreen *screen = screens.at(s);
|
|
|
|
str << '#' << ' ' << s << " \"" << screen->name() << '"'
|
|
|
|
<< " Depth: " << screen->depth()
|
|
|
|
<< " Primary: " << (screen == QGuiApplication::primaryScreen() ? "yes" : "no")
|
|
|
|
<< "\n Geometry: " << screen->geometry() << " Available: " << screen->availableGeometry();
|
|
|
|
if (screen->geometry() != screen->virtualGeometry())
|
|
|
|
str << "\n Virtual geometry: " << screen->virtualGeometry() << " Available: " << screen->availableVirtualGeometry();
|
|
|
|
if (screen->virtualSiblings().size() > 1)
|
|
|
|
str << "\n " << screen->virtualSiblings().size() << " virtual siblings";
|
|
|
|
str << "\n Physical size: " << screen->physicalSize() << " mm"
|
|
|
|
<< " Refresh: " << screen->refreshRate() << " Hz"
|
|
|
|
<< "\n Physical DPI: " << screen->physicalDotsPerInchX()
|
|
|
|
<< ',' << screen->physicalDotsPerInchY()
|
|
|
|
<< " Logical DPI: " << screen->logicalDotsPerInchX()
|
|
|
|
<< ',' << screen->logicalDotsPerInchY()
|
|
|
|
<< "\n DevicePixelRatio: " << screen->devicePixelRatio()
|
|
|
|
<< " Primary orientation: " << screen->primaryOrientation()
|
|
|
|
<< "\n Orientation: " << screen->orientation()
|
2020-03-24 13:15:54 +00:00
|
|
|
<< " Native orientation: " << screen->nativeOrientation() << '\n';
|
2015-01-27 13:40:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// On Windows, this will provide addition GPU info similar to the output of dxdiag.
|
2016-08-23 08:46:09 +00:00
|
|
|
if (QGuiApplication::platformNativeInterface()) {
|
|
|
|
const QVariant gpuInfoV = QGuiApplication::platformNativeInterface()->property("gpu");
|
2020-09-10 15:05:10 +00:00
|
|
|
if (gpuInfoV.userType() == QMetaType::QVariantMap) {
|
2016-08-23 08:46:09 +00:00
|
|
|
const QString description = gpuInfoV.toMap().value(QStringLiteral("printable")).toString();
|
|
|
|
if (!description.isEmpty())
|
|
|
|
str << "\nGPU:\n" << description << "\n\n";
|
|
|
|
}
|
2015-01-27 13:40:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-07 06:42:22 +00:00
|
|
|
void tst_QOpenGlConfig::initTestCase()
|
|
|
|
{
|
|
|
|
if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::OpenGL))
|
|
|
|
QSKIP("OpenGL is not supported on this platform.");
|
|
|
|
}
|
|
|
|
|
2015-01-27 13:40:04 +00:00
|
|
|
void tst_QOpenGlConfig::testConfiguration()
|
|
|
|
{
|
|
|
|
QString result;
|
|
|
|
QTextStream str(&result);
|
|
|
|
dumpConfiguration(str);
|
|
|
|
|
|
|
|
qDebug().noquote() << '\n' << result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dumpGlConfiguration(QOpenGLContext &context, QTextStream &str)
|
|
|
|
{
|
|
|
|
str << "Type : ";
|
|
|
|
#ifdef QT_OPENGL_DYNAMIC
|
|
|
|
str << "Dynamic GL ";
|
|
|
|
#endif
|
|
|
|
switch (context.openGLModuleType()) {
|
|
|
|
case QOpenGLContext::LibGL:
|
|
|
|
str << "LibGL";
|
|
|
|
break;
|
|
|
|
case QOpenGLContext::LibGLES:
|
|
|
|
str << "LibGLES";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
QOpenGLFunctions functions(&context);
|
|
|
|
|
|
|
|
str << "\nVendor : " << reinterpret_cast<const char *>(functions.glGetString(GL_VENDOR))
|
|
|
|
<< "\nRenderer : " << reinterpret_cast<const char *>(functions.glGetString(GL_RENDERER))
|
|
|
|
<< "\nVersion : " << reinterpret_cast<const char *>(functions.glGetString(GL_VERSION))
|
|
|
|
<< "\nShading language : " << reinterpret_cast<const char *>(functions.glGetString(GL_SHADING_LANGUAGE_VERSION))
|
|
|
|
<< "\nFormat : " << context.format();
|
|
|
|
|
2019-05-06 12:00:53 +00:00
|
|
|
QList<QByteArray> extensionList = context.extensions().values();
|
2015-01-27 13:40:04 +00:00
|
|
|
std::sort(extensionList.begin(), extensionList.end());
|
|
|
|
const int extensionCount = extensionList.size();
|
|
|
|
str << "\n\nFound " << extensionCount << " extensions:\n";
|
|
|
|
for (int e = 0; e < extensionCount; ++e)
|
|
|
|
str << ((e % 4) ? ' ' : '\n') << extensionList.at(e);
|
|
|
|
}
|
|
|
|
|
|
|
|
void tst_QOpenGlConfig::testGlConfiguration()
|
|
|
|
{
|
|
|
|
QString result;
|
|
|
|
QTextStream str(&result);
|
|
|
|
|
|
|
|
QWindow window;
|
|
|
|
window.setSurfaceType(QSurface::OpenGLSurface);
|
|
|
|
window.create();
|
|
|
|
QOpenGLContext context;
|
|
|
|
QVERIFY(context.create());
|
|
|
|
QVERIFY(context.makeCurrent(&window));
|
|
|
|
dumpGlConfiguration(context, str);
|
|
|
|
context.doneCurrent();
|
|
|
|
|
|
|
|
qDebug().noquote() << '\n' << result;
|
2015-04-09 14:49:34 +00:00
|
|
|
|
|
|
|
// fromContext either uses the current context or creates a temporary dummy one.
|
|
|
|
QOpenGLConfig::Gpu gpu = QOpenGLConfig::Gpu::fromContext();
|
|
|
|
qDebug().noquote() << '\n' << "GL_VENDOR queried by QOpenGLConfig::Gpu:" << gpu.glVendor;
|
|
|
|
QVERIFY(!gpu.glVendor.isEmpty());
|
2015-01-27 13:40:04 +00:00
|
|
|
}
|
|
|
|
|
2015-02-05 14:45:53 +00:00
|
|
|
static inline QByteArray msgSetMismatch(const QSet<QString> &expected,
|
|
|
|
const QSet<QString> &actual)
|
|
|
|
{
|
2019-05-06 12:00:53 +00:00
|
|
|
const QString result = QStringList(expected.values()).join(QLatin1Char(','))
|
2015-02-05 14:45:53 +00:00
|
|
|
+ QLatin1String(" != ")
|
2019-05-06 12:00:53 +00:00
|
|
|
+ QStringList(actual.values()).join(QLatin1Char(','));
|
2015-02-05 14:45:53 +00:00
|
|
|
return result.toLatin1();
|
|
|
|
}
|
|
|
|
|
|
|
|
void tst_QOpenGlConfig::testBugList()
|
|
|
|
{
|
|
|
|
// Check bug list parsing for some arbitrary NVidia card
|
|
|
|
// faking Windows OS.
|
|
|
|
const QString fileName = QFINDTESTDATA("buglist.json");
|
|
|
|
QVERIFY(!fileName.isEmpty());
|
|
|
|
|
2015-04-09 14:49:34 +00:00
|
|
|
QSet<QString> expectedFeatures;
|
|
|
|
expectedFeatures << "feature1";
|
2015-02-05 14:45:53 +00:00
|
|
|
|
2015-06-23 14:37:16 +00:00
|
|
|
// adapter info
|
2020-06-23 08:04:16 +00:00
|
|
|
QVersionNumber driverVersion(QList<int>() << 9 << 18 << 13 << 4460);
|
2015-06-23 14:37:16 +00:00
|
|
|
QOpenGLConfig::Gpu gpu = QOpenGLConfig::Gpu::fromDevice(0x10DE, 0x0DE9, driverVersion, QByteArrayLiteral("Unknown"));
|
2015-02-05 14:45:53 +00:00
|
|
|
|
2015-04-09 14:49:34 +00:00
|
|
|
QSet<QString> actualFeatures = QOpenGLConfig::gpuFeatures(gpu, QStringLiteral("win"),
|
2015-06-23 14:37:16 +00:00
|
|
|
QVersionNumber(6, 3), QStringLiteral("7"), fileName);
|
2015-04-09 14:49:34 +00:00
|
|
|
QVERIFY2(expectedFeatures == actualFeatures,
|
|
|
|
msgSetMismatch(expectedFeatures, actualFeatures));
|
|
|
|
|
2015-06-23 14:37:16 +00:00
|
|
|
// driver_description
|
|
|
|
gpu = QOpenGLConfig::Gpu::fromDevice(0xDEAD, 0xBEEF, driverVersion, QByteArrayLiteral("Very Long And Special Driver Description"));
|
|
|
|
actualFeatures = QOpenGLConfig::gpuFeatures(gpu, QStringLiteral("win"),
|
|
|
|
QVersionNumber(6, 3), QStringLiteral("8"), fileName);
|
|
|
|
expectedFeatures = QSet<QString>() << "feature2";
|
|
|
|
QVERIFY2(expectedFeatures == actualFeatures,
|
|
|
|
msgSetMismatch(expectedFeatures, actualFeatures));
|
|
|
|
|
|
|
|
// os.release
|
|
|
|
gpu = QOpenGLConfig::Gpu::fromDevice(0xDEAD, 0xBEEF, driverVersion, QByteArrayLiteral("WinVerTest"));
|
|
|
|
actualFeatures = QOpenGLConfig::gpuFeatures(gpu, QStringLiteral("win"),
|
|
|
|
QVersionNumber(12, 34), QStringLiteral("10"), fileName);
|
|
|
|
expectedFeatures = QSet<QString>() << "win10_feature";
|
|
|
|
QVERIFY2(expectedFeatures == actualFeatures,
|
|
|
|
msgSetMismatch(expectedFeatures, actualFeatures));
|
|
|
|
|
|
|
|
// gl_vendor
|
2015-04-09 14:49:34 +00:00
|
|
|
gpu = QOpenGLConfig::Gpu::fromGLVendor(QByteArrayLiteral("Somebody Else"));
|
|
|
|
expectedFeatures.clear();
|
|
|
|
actualFeatures = QOpenGLConfig::gpuFeatures(gpu, QStringLiteral("linux"),
|
2015-06-23 14:37:16 +00:00
|
|
|
QVersionNumber(1, 0), QString(), fileName);
|
2015-04-09 14:49:34 +00:00
|
|
|
QVERIFY2(expectedFeatures == actualFeatures,
|
|
|
|
msgSetMismatch(expectedFeatures, actualFeatures));
|
|
|
|
|
|
|
|
gpu = QOpenGLConfig::Gpu::fromGLVendor(QByteArrayLiteral("The Qt Company"));
|
|
|
|
expectedFeatures = QSet<QString>() << "cool_feature";
|
|
|
|
actualFeatures = QOpenGLConfig::gpuFeatures(gpu, QStringLiteral("linux"),
|
2015-06-23 14:37:16 +00:00
|
|
|
QVersionNumber(1, 0), QString(), fileName);
|
2015-02-05 14:45:53 +00:00
|
|
|
QVERIFY2(expectedFeatures == actualFeatures,
|
|
|
|
msgSetMismatch(expectedFeatures, actualFeatures));
|
|
|
|
}
|
|
|
|
|
2015-08-07 07:42:41 +00:00
|
|
|
void tst_QOpenGlConfig::testDefaultWindowsBlacklist()
|
|
|
|
{
|
2015-09-24 07:25:25 +00:00
|
|
|
if (QGuiApplication::platformName().compare(QLatin1String("windows"), Qt::CaseInsensitive))
|
2015-08-07 07:42:41 +00:00
|
|
|
QSKIP("Only applicable to Windows");
|
|
|
|
|
|
|
|
QFile f(QStringLiteral(":/qt-project.org/windows/openglblacklists/default.json"));
|
|
|
|
QVERIFY(f.open(QIODevice::ReadOnly | QIODevice::Text));
|
|
|
|
QJsonParseError err;
|
|
|
|
QJsonDocument doc = QJsonDocument::fromJson(f.readAll(), &err);
|
|
|
|
QVERIFY2(err.error == 0,
|
|
|
|
QStringLiteral("Failed to parse built-in Windows GPU blacklist. %1 : %2")
|
|
|
|
.arg(err.offset).arg(err.errorString()).toLatin1());
|
|
|
|
}
|
|
|
|
|
2015-01-27 13:40:04 +00:00
|
|
|
QTEST_MAIN(tst_QOpenGlConfig)
|
|
|
|
|
|
|
|
#include "tst_qopenglconfig.moc"
|