// Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 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() << '\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.userType() == QMetaType::QVariantMap) { 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(functions.glGetString(GL_VENDOR)) << "\nRenderer : " << reinterpret_cast(functions.glGetString(GL_RENDERER)) << "\nVersion : " << reinterpret_cast(functions.glGetString(GL_VERSION)) << "\nShading language : " << reinterpret_cast(functions.glGetString(GL_SHADING_LANGUAGE_VERSION)) << "\nFormat : " << context.format(); QList 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 &expected, const QSet &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 expectedFeatures; expectedFeatures << "feature1"; // adapter info QVersionNumber driverVersion(QList() << 9 << 18 << 13 << 4460); QOpenGLConfig::Gpu gpu = QOpenGLConfig::Gpu::fromDevice(0x10DE, 0x0DE9, driverVersion, QByteArrayLiteral("Unknown")); QSet 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() << "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() << "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() << "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"