1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#include <QtGui/QOpenGLFunctions>
30#include <QtGui/QScreen>
31#include <QtGui/QWindow>
32#include <private/qguiapplication_p.h>
33#include <qpa/qplatformintegration.h>
34#include <qpa/qplatformnativeinterface.h>
35#include <private/qopengl_p.h>
36
37#include <QtTest/QtTest>
38
39#include <QtCore/QSysInfo>
40#include <QtCore/QLibraryInfo>
41#include <QtCore/QScopedPointer>
42#include <QtCore/QVariant>
43#include <QtCore/QDebug>
44#include <QtCore/QTextStream>
45#include <QtCore/QJsonDocument>
46
47#include <algorithm>
48
49#define DUMP_CAPABILITY(str, integration, capability) \
50 if (platformIntegration->hasCapability(QPlatformIntegration::capability)) \
51 str << ' ' << #capability;
52
53QTextStream &operator<<(QTextStream &str, const QSize &s)
54{
55 str << s.width() << 'x' << s.height();
56 return str;
57}
58
59QTextStream &operator<<(QTextStream &str, const QRect &r)
60{
61 str << r.size() << '+' << r.x() << '+' << r.y();
62 return str;
63}
64
65QTextStream &operator<<(QTextStream &str, const QSizeF &s)
66{
67 str << s.width() << 'x' << s.height();
68 return str;
69}
70
71QTextStream &operator<<(QTextStream &str, const QSurfaceFormat &format)
72{
73 str << "Version: " << format.majorVersion() << '.'
74 << format.minorVersion() << " Profile: " << format.profile()
75 << " Swap behavior: " << format.swapBehavior()
76 << " Buffer size (RGB";
77 if (format.hasAlpha())
78 str << 'A';
79 str << "): " << format.redBufferSize() << ',' << format.greenBufferSize()
80 << ',' << format.blueBufferSize();
81 if (format.hasAlpha())
82 str << ',' << format.alphaBufferSize();
83 if (const int dbs = format.depthBufferSize())
84 str << " Depth buffer: " << dbs;
85 if (const int sbs = format.stencilBufferSize())
86 str << " Stencil buffer: " << sbs;
87 const int samples = format.samples();
88 if (samples > 0)
89 str << " Samples: " << samples;
90 return str;
91}
92
93/* This test contains code from the qtdiag tool. Its purpose is to output the
94 * graphics configuration to the CI log and to verify that Open GL can be
95 * initialized for platforms on which the qopengl test is marked as
96 * insignificant. */
97
98class tst_QOpenGlConfig : public QObject
99{
100 Q_OBJECT
101
102private slots:
103 void initTestCase();
104 void testConfiguration();
105 void testGlConfiguration();
106 void testBugList();
107 void testDefaultWindowsBlacklist();
108};
109
110static void dumpConfiguration(QTextStream &str)
111{
112 const QPlatformIntegration *platformIntegration = QGuiApplicationPrivate::platformIntegration();
113 str << "\nBuild : " << QLibraryInfo::build()
114 << "\nPlatform : " << QGuiApplication::platformName()
115 << "\nOS : " << QSysInfo::prettyProductName() << " ["
116 << QSysInfo::kernelType() << " version " << QSysInfo::kernelVersion() << ']'
117 << "\nArchitecture : " << QSysInfo::currentCpuArchitecture()
118 << "\nCapabilities :";
119 DUMP_CAPABILITY(str, platformIntegration, ThreadedPixmaps)
120 DUMP_CAPABILITY(str, platformIntegration, OpenGL)
121 DUMP_CAPABILITY(str, platformIntegration, ThreadedOpenGL)
122 DUMP_CAPABILITY(str, platformIntegration, SharedGraphicsCache)
123 DUMP_CAPABILITY(str, platformIntegration, BufferQueueingOpenGL)
124 DUMP_CAPABILITY(str, platformIntegration, WindowMasks)
125 DUMP_CAPABILITY(str, platformIntegration, RasterGLSurface)
126 DUMP_CAPABILITY(str, platformIntegration, AllGLFunctionsQueryable)
127 str << '\n';
128
129 const QList<QScreen*> screens = QGuiApplication::screens();
130 const int screenCount = screens.size();
131 str << "\nScreens: " << screenCount << '\n';
132 for (int s = 0; s < screenCount; ++s) {
133 const QScreen *screen = screens.at(i: s);
134 str << '#' << ' ' << s << " \"" << screen->name() << '"'
135 << " Depth: " << screen->depth()
136 << " Primary: " << (screen == QGuiApplication::primaryScreen() ? "yes" : "no")
137 << "\n Geometry: " << screen->geometry() << " Available: " << screen->availableGeometry();
138 if (screen->geometry() != screen->virtualGeometry())
139 str << "\n Virtual geometry: " << screen->virtualGeometry() << " Available: " << screen->availableVirtualGeometry();
140 if (screen->virtualSiblings().size() > 1)
141 str << "\n " << screen->virtualSiblings().size() << " virtual siblings";
142 str << "\n Physical size: " << screen->physicalSize() << " mm"
143 << " Refresh: " << screen->refreshRate() << " Hz"
144 << "\n Physical DPI: " << screen->physicalDotsPerInchX()
145 << ',' << screen->physicalDotsPerInchY()
146 << " Logical DPI: " << screen->logicalDotsPerInchX()
147 << ',' << screen->logicalDotsPerInchY()
148 << "\n DevicePixelRatio: " << screen->devicePixelRatio()
149 << " Primary orientation: " << screen->primaryOrientation()
150 << "\n Orientation: " << screen->orientation()
151 << " Native orientation: " << screen->nativeOrientation()
152 << " OrientationUpdateMask: " << screen->orientationUpdateMask() << '\n';
153 }
154
155 // On Windows, this will provide addition GPU info similar to the output of dxdiag.
156 if (QGuiApplication::platformNativeInterface()) {
157 const QVariant gpuInfoV = QGuiApplication::platformNativeInterface()->property(name: "gpu");
158 if (gpuInfoV.type() == QVariant::Map) {
159 const QString description = gpuInfoV.toMap().value(QStringLiteral("printable")).toString();
160 if (!description.isEmpty())
161 str << "\nGPU:\n" << description << "\n\n";
162 }
163 }
164}
165
166void tst_QOpenGlConfig::initTestCase()
167{
168 if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(cap: QPlatformIntegration::OpenGL))
169 QSKIP("OpenGL is not supported on this platform.");
170}
171
172void tst_QOpenGlConfig::testConfiguration()
173{
174 QString result;
175 QTextStream str(&result);
176 dumpConfiguration(str);
177
178 qDebug().noquote() << '\n' << result;
179}
180
181static void dumpGlConfiguration(QOpenGLContext &context, QTextStream &str)
182{
183 str << "Type : ";
184#ifdef QT_OPENGL_DYNAMIC
185 str << "Dynamic GL ";
186#endif
187 switch (context.openGLModuleType()) {
188 case QOpenGLContext::LibGL:
189 str << "LibGL";
190 break;
191 case QOpenGLContext::LibGLES:
192 str << "LibGLES";
193 break;
194 }
195 QOpenGLFunctions functions(&context);
196
197 str << "\nVendor : " << reinterpret_cast<const char *>(functions.glGetString(GL_VENDOR))
198 << "\nRenderer : " << reinterpret_cast<const char *>(functions.glGetString(GL_RENDERER))
199 << "\nVersion : " << reinterpret_cast<const char *>(functions.glGetString(GL_VERSION))
200 << "\nShading language : " << reinterpret_cast<const char *>(functions.glGetString(GL_SHADING_LANGUAGE_VERSION))
201 << "\nFormat : " << context.format();
202
203 QList<QByteArray> extensionList = context.extensions().values();
204 std::sort(first: extensionList.begin(), last: extensionList.end());
205 const int extensionCount = extensionList.size();
206 str << "\n\nFound " << extensionCount << " extensions:\n";
207 for (int e = 0; e < extensionCount; ++e)
208 str << ((e % 4) ? ' ' : '\n') << extensionList.at(i: e);
209}
210
211void tst_QOpenGlConfig::testGlConfiguration()
212{
213 QString result;
214 QTextStream str(&result);
215
216 QWindow window;
217 window.setSurfaceType(QSurface::OpenGLSurface);
218 window.create();
219 QOpenGLContext context;
220 QVERIFY(context.create());
221 QVERIFY(context.makeCurrent(&window));
222 dumpGlConfiguration(context, str);
223 context.doneCurrent();
224
225 qDebug().noquote() << '\n' << result;
226
227 // fromContext either uses the current context or creates a temporary dummy one.
228 QOpenGLConfig::Gpu gpu = QOpenGLConfig::Gpu::fromContext();
229 qDebug().noquote() << '\n' << "GL_VENDOR queried by QOpenGLConfig::Gpu:" << gpu.glVendor;
230 QVERIFY(!gpu.glVendor.isEmpty());
231}
232
233static inline QByteArray msgSetMismatch(const QSet<QString> &expected,
234 const QSet<QString> &actual)
235{
236 const QString result = QStringList(expected.values()).join(sep: QLatin1Char(','))
237 + QLatin1String(" != ")
238 + QStringList(actual.values()).join(sep: QLatin1Char(','));
239 return result.toLatin1();
240}
241
242void tst_QOpenGlConfig::testBugList()
243{
244 // Check bug list parsing for some arbitrary NVidia card
245 // faking Windows OS.
246 const QString fileName = QFINDTESTDATA("buglist.json");
247 QVERIFY(!fileName.isEmpty());
248
249 QSet<QString> expectedFeatures;
250 expectedFeatures << "feature1";
251
252 // adapter info
253 QVersionNumber driverVersion(QVector<int>() << 9 << 18 << 13 << 4460);
254 QOpenGLConfig::Gpu gpu = QOpenGLConfig::Gpu::fromDevice(vendorId: 0x10DE, deviceId: 0x0DE9, driverVersion, QByteArrayLiteral("Unknown"));
255
256 QSet<QString> actualFeatures = QOpenGLConfig::gpuFeatures(gpu, QStringLiteral("win"),
257 kernelVersion: QVersionNumber(6, 3), QStringLiteral("7"), fileName);
258 QVERIFY2(expectedFeatures == actualFeatures,
259 msgSetMismatch(expectedFeatures, actualFeatures));
260
261 // driver_description
262 gpu = QOpenGLConfig::Gpu::fromDevice(vendorId: 0xDEAD, deviceId: 0xBEEF, driverVersion, QByteArrayLiteral("Very Long And Special Driver Description"));
263 actualFeatures = QOpenGLConfig::gpuFeatures(gpu, QStringLiteral("win"),
264 kernelVersion: QVersionNumber(6, 3), QStringLiteral("8"), fileName);
265 expectedFeatures = QSet<QString>() << "feature2";
266 QVERIFY2(expectedFeatures == actualFeatures,
267 msgSetMismatch(expectedFeatures, actualFeatures));
268
269 // os.release
270 gpu = QOpenGLConfig::Gpu::fromDevice(vendorId: 0xDEAD, deviceId: 0xBEEF, driverVersion, QByteArrayLiteral("WinVerTest"));
271 actualFeatures = QOpenGLConfig::gpuFeatures(gpu, QStringLiteral("win"),
272 kernelVersion: QVersionNumber(12, 34), QStringLiteral("10"), fileName);
273 expectedFeatures = QSet<QString>() << "win10_feature";
274 QVERIFY2(expectedFeatures == actualFeatures,
275 msgSetMismatch(expectedFeatures, actualFeatures));
276
277 // gl_vendor
278 gpu = QOpenGLConfig::Gpu::fromGLVendor(QByteArrayLiteral("Somebody Else"));
279 expectedFeatures.clear();
280 actualFeatures = QOpenGLConfig::gpuFeatures(gpu, QStringLiteral("linux"),
281 kernelVersion: QVersionNumber(1, 0), osVersion: QString(), fileName);
282 QVERIFY2(expectedFeatures == actualFeatures,
283 msgSetMismatch(expectedFeatures, actualFeatures));
284
285 gpu = QOpenGLConfig::Gpu::fromGLVendor(QByteArrayLiteral("The Qt Company"));
286 expectedFeatures = QSet<QString>() << "cool_feature";
287 actualFeatures = QOpenGLConfig::gpuFeatures(gpu, QStringLiteral("linux"),
288 kernelVersion: QVersionNumber(1, 0), osVersion: QString(), fileName);
289 QVERIFY2(expectedFeatures == actualFeatures,
290 msgSetMismatch(expectedFeatures, actualFeatures));
291}
292
293void tst_QOpenGlConfig::testDefaultWindowsBlacklist()
294{
295 if (QGuiApplication::platformName().compare(other: QLatin1String("windows"), cs: Qt::CaseInsensitive))
296 QSKIP("Only applicable to Windows");
297
298 QFile f(QStringLiteral(":/qt-project.org/windows/openglblacklists/default.json"));
299 QVERIFY(f.open(QIODevice::ReadOnly | QIODevice::Text));
300 QJsonParseError err;
301 QJsonDocument doc = QJsonDocument::fromJson(json: f.readAll(), error: &err);
302 QVERIFY2(err.error == 0,
303 QStringLiteral("Failed to parse built-in Windows GPU blacklist. %1 : %2")
304 .arg(err.offset).arg(err.errorString()).toLatin1());
305}
306
307QTEST_MAIN(tst_QOpenGlConfig)
308
309#include "tst_qopenglconfig.moc"
310

source code of qtbase/tests/auto/gui/qopenglconfig/tst_qopenglconfig.cpp