Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qwindowsopengltester.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5#include "qwindowscontext.h"
6
7#include <QtCore/qvariant.h>
8#include <QtCore/qmap.h>
9#include <QtCore/qdebug.h>
10#include <QtCore/qtextstream.h>
11#include <QtCore/qcoreapplication.h>
12#include <QtCore/qfile.h>
13#include <QtCore/qfileinfo.h>
14#include <QtCore/qstandardpaths.h>
15#include <QtCore/qlibraryinfo.h>
16#include <QtCore/qhash.h>
17#include <private/qsystemlibrary_p.h>
18#include <QtGui/qtgui-config.h>
19
20#ifndef QT_NO_OPENGL
21#include <private/qopengl_p.h>
22#endif
23
24#include <QtCore/qt_windows.h>
25#include <d3d9.h>
26
28
29static const DWORD VENDOR_ID_AMD = 0x1002;
30
31static GpuDescription adapterIdentifierToGpuDescription(const D3DADAPTER_IDENTIFIER9 &adapterIdentifier)
32{
34 result.vendorId = adapterIdentifier.VendorId;
35 result.deviceId = adapterIdentifier.DeviceId;
36 result.revision = adapterIdentifier.Revision;
37 result.subSysId = adapterIdentifier.SubSysId;
38 QList<int> version(4, 0);
39 version[0] = HIWORD(adapterIdentifier.DriverVersion.HighPart); // Product
40 version[1] = LOWORD(adapterIdentifier.DriverVersion.HighPart); // Version
41 version[2] = HIWORD(adapterIdentifier.DriverVersion.LowPart); // Sub version
42 version[3] = LOWORD(adapterIdentifier.DriverVersion.LowPart); // build
43 result.driverVersion = QVersionNumber(version);
44 result.driverName = adapterIdentifier.Driver;
45 result.description = adapterIdentifier.Description;
46 return result;
47}
48
50{
51public:
52 Q_DISABLE_COPY_MOVE(QDirect3D9Handle)
53
56
57 bool isValid() const { return m_direct3D9 != nullptr; }
58
59 UINT adapterCount() const { return m_direct3D9 ? m_direct3D9->GetAdapterCount() : 0u; }
60 bool retrieveAdapterIdentifier(UINT n, D3DADAPTER_IDENTIFIER9 *adapterIdentifier) const;
61
62private:
63 IDirect3D9 *m_direct3D9 = nullptr;
64};
65
67{
68 m_direct3D9 = Direct3DCreate9(D3D_SDK_VERSION);
69}
70
72{
73 if (m_direct3D9)
74 m_direct3D9->Release();
75}
76
77bool QDirect3D9Handle::retrieveAdapterIdentifier(UINT n, D3DADAPTER_IDENTIFIER9 *adapterIdentifier) const
78{
79 return m_direct3D9
80 && SUCCEEDED(m_direct3D9->GetAdapterIdentifier(n, 0, adapterIdentifier));
81}
82
84{
86 QDirect3D9Handle direct3D9;
87 if (!direct3D9.isValid())
88 return result;
89
90 D3DADAPTER_IDENTIFIER9 adapterIdentifier;
91 bool isAMD = false;
92 // Adapter "0" is D3DADAPTER_DEFAULT which returns the default adapter. In
93 // multi-GPU, multi-screen setups this is the GPU that is associated with
94 // the "main display" in the Display Settings, and this is the GPU OpenGL
95 // and D3D uses by default. Therefore querying any additional adapters is
96 // futile and not useful for our purposes in general, except for
97 // identifying a few special cases later on.
98 if (direct3D9.retrieveAdapterIdentifier(0, &adapterIdentifier)) {
99 result = adapterIdentifierToGpuDescription(adapterIdentifier);
100 isAMD = result.vendorId == VENDOR_ID_AMD;
101 }
102
103 // Detect QTBUG-50371 (having AMD as the default adapter results in a crash
104 // when starting apps on a screen connected to the Intel card) by looking
105 // for a default AMD adapter and an additional non-AMD one.
106 if (isAMD) {
107 const UINT adapterCount = direct3D9.adapterCount();
108 for (UINT adp = 1; adp < adapterCount; ++adp) {
109 if (direct3D9.retrieveAdapterIdentifier(adp, &adapterIdentifier)
110 && adapterIdentifier.VendorId != VENDOR_ID_AMD) {
111 // Bingo. Now figure out the display for the AMD card.
112 DISPLAY_DEVICE dd;
113 memset(&dd, 0, sizeof(dd));
114 dd.cb = sizeof(dd);
115 for (int dev = 0; EnumDisplayDevices(nullptr, dev, &dd, 0); ++dev) {
116 if (dd.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) {
117 // DeviceName is something like \\.\DISPLAY1 which can be used to
118 // match with the MONITORINFOEX::szDevice queried by QWindowsScreen.
119 result.gpuSuitableScreen = QString::fromWCharArray(dd.DeviceName);
120 break;
121 }
122 }
123 break;
124 }
125 }
126 }
127
128 return result;
129}
130
131QList<GpuDescription> GpuDescription::detectAll()
132{
133 QList<GpuDescription> result;
134 QDirect3D9Handle direct3D9;
135 if (const UINT adapterCount = direct3D9.adapterCount()) {
136 for (UINT adp = 0; adp < adapterCount; ++adp) {
137 D3DADAPTER_IDENTIFIER9 adapterIdentifier;
138 if (direct3D9.retrieveAdapterIdentifier(adp, &adapterIdentifier))
139 result.append(adapterIdentifierToGpuDescription(adapterIdentifier));
140 }
141 }
142 return result;
143}
144
145#ifndef QT_NO_DEBUG_STREAM
147{
149 d.nospace();
150 d << Qt::hex << Qt::showbase << "GpuDescription(vendorId=" << gd.vendorId
151 << ", deviceId=" << gd.deviceId << ", subSysId=" << gd.subSysId
152 << Qt::dec << Qt::noshowbase << ", revision=" << gd.revision
153 << ", driver: " << gd.driverName
154 << ", version=" << gd.driverVersion << ", " << gd.description
155 << gd.gpuSuitableScreen << ')';
156 return d;
157}
158#endif // !QT_NO_DEBUG_STREAM
159
160// Return printable string formatted like the output of the dxdiag tool.
162{
165 str << " Card name : " << description
166 << "\n Driver Name : " << driverName
167 << "\n Driver Version : " << driverVersion.toString()
168 << "\n Vendor ID : 0x" << qSetPadChar(u'0')
170 << "\n Device ID : 0x" << qSetFieldWidth(4) << deviceId
171 << "\n SubSys ID : 0x" << qSetFieldWidth(8) << subSysId
172 << "\n Revision ID : 0x" << qSetFieldWidth(4) << revision
173 << Qt::dec;
175 str << "\nGL windows forced to screen: " << gpuSuitableScreen;
176 return result;
177}
178
180{
183 result.insert(QStringLiteral("deviceId"), QVariant(deviceId));
184 result.insert(QStringLiteral("subSysId"),QVariant(subSysId));
185 result.insert(QStringLiteral("revision"), QVariant(revision));
187 result.insert(QStringLiteral("driverProduct"), QVariant(driverVersion.segmentAt(0)));
188 result.insert(QStringLiteral("driverVersion"), QVariant(driverVersion.segmentAt(1)));
189 result.insert(QStringLiteral("driverSubVersion"), QVariant(driverVersion.segmentAt(2)));
190 result.insert(QStringLiteral("driverBuild"), QVariant(driverVersion.segmentAt(3)));
191 result.insert(QStringLiteral("driverVersionString"), driverVersion.toString());
193 result.insert(QStringLiteral("printable"), QVariant(toString()));
194 return result;
195}
196
198{
199 const char openGlVar[] = "QT_OPENGL";
201 qWarning("Qt::AA_UseOpenGLES is no longer supported in Qt 6");
206 if (qEnvironmentVariableIsSet(openGlVar)) {
207 const QByteArray requested = qgetenv(openGlVar);
208 if (requested == "angle")
209 qWarning("QT_OPENGL=angle is no longer supported in Qt 6");
210 if (requested == "desktop")
212 if (requested == "software")
214 qCWarning(lcQpaGl) << "Invalid value set for " << openGlVar << ": " << requested;
215 }
217}
218
220{
221 if (QFileInfo(fileName).isAbsolute())
222 return fileName;
223 // Try QLibraryInfo::SettingsPath which is typically empty unless specified in qt.conf,
224 // then resolve via QStandardPaths::ConfigLocation.
226 if (!settingsPath.isEmpty()) { // SettingsPath is empty unless specified in qt.conf.
227 const QFileInfo fi(settingsPath + u'/' + fileName);
228 if (fi.isFile())
229 return fi.absoluteFilePath();
230 }
232}
233
234#ifndef QT_NO_OPENGL
235typedef QHash<QOpenGLConfig::Gpu, QWindowsOpenGLTester::Renderers> SupportedRenderersCache;
236Q_GLOBAL_STATIC(SupportedRenderersCache, supportedRenderersCache)
237#endif
238
239QWindowsOpenGLTester::Renderers QWindowsOpenGLTester::detectSupportedRenderers(const GpuDescription &gpu,
240 Renderer requested)
241{
242#if defined(QT_NO_OPENGL)
243 Q_UNUSED(gpu);
244 Q_UNUSED(requested);
245 return {};
246#else
248 SupportedRenderersCache *srCache = supportedRenderersCache();
249 SupportedRenderersCache::const_iterator it = srCache->constFind(qgpu);
250 if (it != srCache->cend())
251 return *it;
252
253 QWindowsOpenGLTester::Renderers result(QWindowsOpenGLTester::SoftwareRasterizer);
254
255 // Don't test for GL if explicitly requested or GLES only is requested
256 if (requested == DesktopGl || testDesktopGL())
258
259 QSet<QString> features; // empty by default -> nothing gets disabled
260 if (!qEnvironmentVariableIsSet("QT_NO_OPENGL_BUGLIST")) {
261 const char bugListFileVar[] = "QT_OPENGL_BUGLIST";
262 QString buglistFileName = QStringLiteral(":/qt-project.org/windows/openglblacklists/default.json");
263 if (qEnvironmentVariableIsSet(bugListFileVar)) {
265 if (!fileName.isEmpty())
266 buglistFileName = fileName;
267 }
268 features = QOpenGLConfig::gpuFeatures(qgpu, buglistFileName);
269 }
270 qCDebug(lcQpaGl) << "GPU features:" << features;
271
272 if (features.contains(QStringLiteral("disable_desktopgl"))) { // Qt-specific
273 qCDebug(lcQpaGl) << "Disabling Desktop GL: " << gpu;
275 }
276 if (features.contains(QStringLiteral("disable_rotation"))) {
277 qCDebug(lcQpaGl) << "Disabling rotation: " << gpu;
279 }
280 if (features.contains(QStringLiteral("disable_program_cache"))) {
281 qCDebug(lcQpaGl) << "Disabling program cache: " << gpu;
283 }
284 srCache->insert(qgpu, result);
285 return result;
286#endif // !QT_NO_OPENGL
287}
288
289QWindowsOpenGLTester::Renderers QWindowsOpenGLTester::supportedRenderers(Renderer requested)
290{
292 const QWindowsOpenGLTester::Renderers result = detectSupportedRenderers(gpu, requested);
293 qCDebug(lcQpaGl) << __FUNCTION__ << gpu << requested << "renderer: " << result;
294 return result;
295}
296
297bool QWindowsOpenGLTester::testDesktopGL()
298{
299#if !defined(QT_NO_OPENGL)
300 typedef HGLRC (WINAPI *CreateContextType)(HDC);
301 typedef BOOL (WINAPI *DeleteContextType)(HGLRC);
302 typedef BOOL (WINAPI *MakeCurrentType)(HDC, HGLRC);
303 typedef PROC (WINAPI *WglGetProcAddressType)(LPCSTR);
304
305 HMODULE lib = nullptr;
306 HWND wnd = nullptr;
307 HDC dc = nullptr;
308 HGLRC context = nullptr;
309 LPCTSTR className = L"qtopengltest";
310
311 CreateContextType CreateContext = nullptr;
312 DeleteContextType DeleteContext = nullptr;
313 MakeCurrentType MakeCurrent = nullptr;
314 WglGetProcAddressType WGL_GetProcAddress = nullptr;
315
316 bool result = false;
317
318 // Test #1: Load opengl32.dll and try to resolve an OpenGL 2 function.
319 // This will typically fail on systems that do not have a real OpenGL driver.
320 lib = QSystemLibrary::load(L"opengl32");
321 if (lib) {
322 CreateContext = reinterpret_cast<CreateContextType>(
323 reinterpret_cast<QFunctionPointer>(::GetProcAddress(lib, "wglCreateContext")));
324 if (!CreateContext)
325 goto cleanup;
326 DeleteContext = reinterpret_cast<DeleteContextType>(
327 reinterpret_cast<QFunctionPointer>(::GetProcAddress(lib, "wglDeleteContext")));
328 if (!DeleteContext)
329 goto cleanup;
330 MakeCurrent = reinterpret_cast<MakeCurrentType>(
331 reinterpret_cast<QFunctionPointer>(::GetProcAddress(lib, "wglMakeCurrent")));
332 if (!MakeCurrent)
333 goto cleanup;
334 WGL_GetProcAddress = reinterpret_cast<WglGetProcAddressType>(
335 reinterpret_cast<QFunctionPointer>(::GetProcAddress(lib, "wglGetProcAddress")));
336 if (!WGL_GetProcAddress)
337 goto cleanup;
338
339 WNDCLASS wclass;
340 wclass.cbClsExtra = 0;
341 wclass.cbWndExtra = 0;
342 wclass.hInstance = static_cast<HINSTANCE>(GetModuleHandle(nullptr));
343 wclass.hIcon = nullptr;
344 wclass.hCursor = nullptr;
345 wclass.hbrBackground = HBRUSH(COLOR_BACKGROUND);
346 wclass.lpszMenuName = nullptr;
347 wclass.lpfnWndProc = DefWindowProc;
348 wclass.lpszClassName = className;
349 wclass.style = CS_OWNDC;
350 if (!RegisterClass(&wclass))
351 goto cleanup;
352 wnd = CreateWindow(className, L"qtopenglproxytest", WS_OVERLAPPED,
353 0, 0, 640, 480, nullptr, nullptr, wclass.hInstance, nullptr);
354 if (!wnd)
355 goto cleanup;
356 dc = GetDC(wnd);
357 if (!dc)
358 goto cleanup;
359
360 PIXELFORMATDESCRIPTOR pfd;
361 memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR));
362 pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
363 pfd.nVersion = 1;
364 pfd.dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_GENERIC_FORMAT;
365 pfd.iPixelType = PFD_TYPE_RGBA;
366 // Use the GDI functions. Under the hood this will call the wgl variants in opengl32.dll.
367 int pixelFormat = ChoosePixelFormat(dc, &pfd);
368 if (!pixelFormat)
369 goto cleanup;
370 if (!SetPixelFormat(dc, pixelFormat, &pfd))
371 goto cleanup;
372 context = CreateContext(dc);
373 if (!context)
374 goto cleanup;
375 if (!MakeCurrent(dc, context))
376 goto cleanup;
377
378 // Now that there is finally a context current, try doing something useful.
379
380 // Check the version. If we got 1.x then it's all hopeless and we can stop right here.
381 typedef const GLubyte * (APIENTRY * GetString_t)(GLenum name);
382 auto GetString = reinterpret_cast<GetString_t>(
383 reinterpret_cast<QFunctionPointer>(::GetProcAddress(lib, "glGetString")));
384 if (GetString) {
385 if (const char *versionStr = reinterpret_cast<const char *>(GetString(GL_VERSION))) {
386 const QByteArray version(versionStr);
387 const int majorDot = version.indexOf('.');
388 if (majorDot != -1) {
389 int minorDot = version.indexOf('.', majorDot + 1);
390 if (minorDot == -1)
391 minorDot = version.size();
392 const int major = version.mid(0, majorDot).toInt();
393 const int minor = version.mid(majorDot + 1, minorDot - majorDot - 1).toInt();
394 qCDebug(lcQpaGl, "Basic wglCreateContext gives version %d.%d", major, minor);
395 // Try to be as lenient as possible. Missing version, bogus values and
396 // such are all accepted. The driver may still be functional. Only
397 // check for known-bad cases, like versions "1.4.0 ...".
398 if (major == 1) {
399 result = false;
400 qCDebug(lcQpaGl, "OpenGL version too low");
401 }
402 }
403 }
404 } else {
405 result = false;
406 qCDebug(lcQpaGl, "OpenGL 1.x entry points not found");
407 }
408
409 // Check for a shader-specific function.
410 if (WGL_GetProcAddress("glCreateShader")) {
411 result = true;
412 qCDebug(lcQpaGl, "OpenGL 2.0 entry points available");
413 } else {
414 qCDebug(lcQpaGl, "OpenGL 2.0 entry points not found");
415 }
416 } else {
417 qCDebug(lcQpaGl, "Failed to load opengl32.dll");
418 }
419
420cleanup:
421 if (MakeCurrent)
422 MakeCurrent(nullptr, nullptr);
423 if (context)
424 DeleteContext(context);
425 if (dc && wnd)
426 ReleaseDC(wnd, dc);
427 if (wnd) {
428 DestroyWindow(wnd);
429 UnregisterClass(className, GetModuleHandle(nullptr));
430 }
431 // No FreeLibrary. Some implementations, Mesa in particular, deadlock when trying to unload.
432
433 return result;
434#else
435 return false;
436#endif // !QT_NO_OPENGL
437}
438
\inmodule QtCore
Definition qbytearray.h:57
static bool testAttribute(Qt::ApplicationAttribute attribute)
Returns true if attribute attribute is set; otherwise returns false.
\inmodule QtCore
\inmodule QtCore
bool retrieveAdapterIdentifier(UINT n, D3DADAPTER_IDENTIFIER9 *adapterIdentifier) const
static QString decodeName(const QByteArray &localFileName)
This does the reverse of QFile::encodeName() using localFileName.
Definition qfile.h:162
static QString path(LibraryPath p)
iterator insert(const Key &key, const T &value)
Definition qmap.h:688
static QSet< QString > gpuFeatures(const Gpu &gpu, const QString &osName, const QVersionNumber &kernelVersion, const QString &osVersion, const QJsonDocument &doc)
Definition qopengl.cpp:424
const_iterator cend() const noexcept
Definition qset.h:142
const_iterator constFind(const T &value) const
Definition qset.h:161
static QString locate(StandardLocation type, const QString &fileName, LocateOptions options=LocateFile)
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
static QString fromWCharArray(const wchar_t *string, qsizetype size=-1)
Definition qstring.h:1309
\inmodule QtCore
\inmodule QtCore
Definition qvariant.h:65
\inmodule QtCore
Q_CORE_EXPORT QString toString() const
Returns a string with all of the segments delimited by a period ({.}).
int segmentAt(qsizetype index) const noexcept
Returns the segment value at index.
static Renderer requestedRenderer()
static QWindowsOpenGLTester::Renderers supportedRenderers(Renderer requested)
QString str
[2]
QSet< QString >::iterator it
Combined button and popup list for selecting options.
QTextStream & hex(QTextStream &stream)
Calls QTextStream::setIntegerBase(16) on stream and returns stream.
QTextStream & showbase(QTextStream &stream)
Calls QTextStream::setNumberFlags(QTextStream::numberFlags() | QTextStream::ShowBase) on stream and r...
QTextStream & uppercasedigits(QTextStream &stream)
Calls QTextStream::setNumberFlags(QTextStream::numberFlags() | QTextStream::UppercaseDigits) on strea...
QTextStream & noshowbase(QTextStream &stream)
Calls QTextStream::setNumberFlags(QTextStream::numberFlags() & ~QTextStream::ShowBase) on stream and ...
QTextStream & dec(QTextStream &stream)
Calls QTextStream::setIntegerBase(10) on stream and returns stream.
@ AA_UseSoftwareOpenGL
Definition qnamespace.h:446
@ AA_UseDesktopOpenGL
Definition qnamespace.h:444
@ AA_UseOpenGLES
Definition qnamespace.h:445
static void * context
#define Q_GLOBAL_STATIC(TYPE, NAME,...)
#define qWarning
Definition qlogging.h:166
#define qCWarning(category,...)
#define qCDebug(category,...)
typedef GLenum(GL_APIENTRYP PFNGLGETGRAPHICSRESETSTATUSKHRPROC)(void)
GLuint name
GLfloat n
GLdouble s
[6]
Definition qopenglext.h:235
#define APIENTRY
Definition qopenglext.h:51
GLuint64EXT * result
[6]
#define QStringLiteral(str)
Q_CORE_EXPORT QByteArray qgetenv(const char *varName)
Q_CORE_EXPORT bool qEnvironmentVariableIsSet(const char *varName) noexcept
QTextStreamManipulator qSetPadChar(QChar ch)
QTextStreamManipulator qSetFieldWidth(int width)
#define Q_UNUSED(x)
HINSTANCE HMODULE
static GpuDescription adapterIdentifierToGpuDescription(const D3DADAPTER_IDENTIFIER9 &adapterIdentifier)
QHash< QOpenGLConfig::Gpu, QWindowsOpenGLTester::Renderers > SupportedRenderersCache
static QT_BEGIN_NAMESPACE const DWORD VENDOR_ID_AMD
static QString resolveBugListFile(const QString &fileName)
QDebug operator<<(QDebug d, const GpuDescription &gd)
const char className[16]
[1]
Definition qwizard.cpp:100
QString toString() const
static GpuDescription detect()
static QList< GpuDescription > detectAll()
QVariant toVariant() const
QVersionNumber driverVersion
static Gpu fromDevice(uint vendorId, uint deviceId, QVersionNumber driverVersion, const QByteArray &driverDescription)
Definition qopengl_p.h:62