| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the test suite 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 <QtGui/QOpenGLFunctions> |
| #include <QtGui/QScreen> |
| #include <QtGui/QWindow> |
| #include <private/qguiapplication_p.h> |
| #include <qpa/qplatformintegration.h> |
| #include <qpa/qplatformnativeinterface.h> |
| #include <private/qopengl_p.h> |
| |
| #include <QtTest/QtTest> |
| |
| #include <QtCore/QSysInfo> |
| #include <QtCore/QLibraryInfo> |
| #include <QtCore/QScopedPointer> |
| #include <QtCore/QVariant> |
| #include <QtCore/QDebug> |
| #include <QtCore/QTextStream> |
| #include <QtCore/QJsonDocument> |
| |
| #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: |
| void initTestCase(); |
| void testConfiguration(); |
| void testGlConfiguration(); |
| void testBugList(); |
| void testDefaultWindowsBlacklist(); |
| }; |
| |
| 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() |
| << " Native orientation: " << screen->nativeOrientation() |
| << " OrientationUpdateMask: " << screen->orientationUpdateMask() << '\n'; |
| } |
| |
| // On Windows, this will provide addition GPU info similar to the output of dxdiag. |
| if (QGuiApplication::platformNativeInterface()) { |
| const QVariant gpuInfoV = QGuiApplication::platformNativeInterface()->property("gpu"); |
| if (gpuInfoV.type() == QVariant::Map) { |
| const QString description = gpuInfoV.toMap().value(QStringLiteral("printable")).toString(); |
| if (!description.isEmpty()) |
| str << "\nGPU:\n" << description << "\n\n"; |
| } |
| } |
| } |
| |
| void tst_QOpenGlConfig::initTestCase() |
| { |
| if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::OpenGL)) |
| QSKIP("OpenGL is not supported on this platform."); |
| } |
| |
| 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(); |
| |
| QList<QByteArray> extensionList = context.extensions().values(); |
| 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; |
| |
| // 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()); |
| } |
| |
| static inline QByteArray msgSetMismatch(const QSet<QString> &expected, |
| const QSet<QString> &actual) |
| { |
| const QString result = QStringList(expected.values()).join(QLatin1Char(',')) |
| + QLatin1String(" != ") |
| + QStringList(actual.values()).join(QLatin1Char(',')); |
| 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()); |
| |
| QSet<QString> expectedFeatures; |
| expectedFeatures << "feature1"; |
| |
| // adapter info |
| QVersionNumber driverVersion(QVector<int>() << 9 << 18 << 13 << 4460); |
| QOpenGLConfig::Gpu gpu = QOpenGLConfig::Gpu::fromDevice(0x10DE, 0x0DE9, driverVersion, QByteArrayLiteral("Unknown")); |
| |
| QSet<QString> actualFeatures = QOpenGLConfig::gpuFeatures(gpu, QStringLiteral("win"), |
| QVersionNumber(6, 3), QStringLiteral("7"), fileName); |
| QVERIFY2(expectedFeatures == actualFeatures, |
| msgSetMismatch(expectedFeatures, actualFeatures)); |
| |
| // 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 |
| gpu = QOpenGLConfig::Gpu::fromGLVendor(QByteArrayLiteral("Somebody Else")); |
| expectedFeatures.clear(); |
| actualFeatures = QOpenGLConfig::gpuFeatures(gpu, QStringLiteral("linux"), |
| QVersionNumber(1, 0), QString(), fileName); |
| 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"), |
| QVersionNumber(1, 0), QString(), fileName); |
| QVERIFY2(expectedFeatures == actualFeatures, |
| msgSetMismatch(expectedFeatures, actualFeatures)); |
| } |
| |
| void tst_QOpenGlConfig::testDefaultWindowsBlacklist() |
| { |
| if (QGuiApplication::platformName().compare(QLatin1String("windows"), Qt::CaseInsensitive)) |
| 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()); |
| } |
| |
| QTEST_MAIN(tst_QOpenGlConfig) |
| |
| #include "tst_qopenglconfig.moc" |