Branch libreoffice-5-0-4
[LibreOffice.git] / vcl / opengl / win / WinDeviceInfo.cxx
blob768930e463d9ba7bfdac4746231dec6df42b3b3a
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
10 #include "opengl/win/WinDeviceInfo.hxx"
12 #include <windows.h>
13 #include <setupapi.h>
14 #include <algorithm>
15 #include <cstdint>
16 #include <rtl/ustrbuf.hxx>
18 OUString* WinOpenGLDeviceInfo::mpDeviceVendors[wgl::DeviceVendorMax];
19 std::vector<wgl::DriverInfo> WinOpenGLDeviceInfo::maDriverInfo;
21 #define APPEND_TO_DRIVER_BLOCKLIST(os, vendor, devices, driverComparator, driverVersion, suggestedVersion) \
22 maDriverInfo.push_back(wgl::DriverInfo(os, vendor, devices, driverComparator, driverVersion, false, suggestedVersion))
24 #define APPEND_TO_DRIVER_BLOCKLIST2(os, vendor, devices, driverComparator, driverVersion) \
25 maDriverInfo.push_back(wgl::DriverInfo(os, vendor, devices, driverComparator, driverVersion))
27 #define APPEND_TO_DRIVER_WHITELIST(os, vendor, devices, driverComparator, driverVersion) \
28 maDriverInfo.push_back(wgl::DriverInfo(os, vendor, devices, driverComparator, driverVersion, true))
30 #define APPEND_TO_DRIVER_BLOCKLIST_RANGE(os, vendor, devices, driverComparator, driverVersion, driverVersionMax, suggestedVersion) \
31 do { \
32 assert(driverComparator == wgl::DRIVER_BETWEEN_EXCLUSIVE || \
33 driverComparator == wgl::DRIVER_BETWEEN_INCLUSIVE || \
34 driverComparator == wgl::DRIVER_BETWEEN_INCLUSIVE_START); \
35 wgl::DriverInfo info(os, vendor, devices, driverComparator, driverVersion, false, suggestedVersion); \
36 info.mnDriverVersionMax = driverVersionMax; \
37 maDriverInfo.push_back(info); \
38 } while (false)
40 namespace {
43 void GetDLLVersion(const sal_Unicode* aDLLPath, OUString& aVersion)
45 DWORD versInfoSize, vers[4] = {0};
46 // version info not available case
47 aVersion = OUString("0.0.0.0");
48 versInfoSize = GetFileVersionInfoSizeW(aDLLPath, nullptr);
49 std::vector<char> versionInfo(512, 0);
51 if (versInfoSize == 0)
53 return;
55 versionInfo.resize(uint32_t(versInfoSize));
57 if (!GetFileVersionInfoW(aDLLPath, 0, versInfoSize,
58 LPBYTE(&versionInfo[0])))
60 return;
63 UINT len = 0;
64 VS_FIXEDFILEINFO *fileInfo = nullptr;
65 if (!VerQueryValue(LPBYTE(&versionInfo[0]), TEXT("\\"),
66 (LPVOID *)&fileInfo, &len) ||
67 len == 0 ||
68 fileInfo == nullptr)
70 return;
73 DWORD fileVersMS = fileInfo->dwFileVersionMS;
74 DWORD fileVersLS = fileInfo->dwFileVersionLS;
76 vers[0] = HIWORD(fileVersMS);
77 vers[1] = LOWORD(fileVersMS);
78 vers[2] = HIWORD(fileVersLS);
79 vers[3] = LOWORD(fileVersLS);
81 char buf[256];
82 sprintf(buf, "%d.%d.%d.%d", vers[0], vers[1], vers[2], vers[3]);
83 OString aBuf(buf);
84 aVersion = OStringToOUString(aBuf, RTL_TEXTENCODING_UTF8);
88 * * Compute the length of an array with constant length. (Use of this method
89 * * with a non-array pointer will not compile.)
90 * *
91 * * Beware of the implicit trailing '\0' when using this with string constants.
92 * */
93 template<typename T, size_t N>
94 size_t ArrayLength(T (&aArr)[N])
96 (void) aArr;
97 return N;
100 #define GFX_DRIVER_VERSION(a,b,c,d) \
101 ((uint64_t(a)<<48) | (uint64_t(b)<<32) | (uint64_t(c)<<16) | uint64_t(d))
103 bool GetKeyValue(const WCHAR* keyLocation, const WCHAR* keyName, OUString& destString, int type)
105 HKEY key;
106 DWORD dwcbData;
107 DWORD dValue;
108 DWORD resultType;
109 LONG result;
110 bool retval = true;
112 result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, keyLocation, 0, KEY_QUERY_VALUE, &key);
113 if (result != ERROR_SUCCESS) {
114 return false;
117 switch (type) {
118 case REG_DWORD:
120 // We only use this for vram size
121 dwcbData = sizeof(dValue);
122 result = RegQueryValueExW(key, keyName, nullptr, &resultType,
123 (LPBYTE)&dValue, &dwcbData);
124 if (result == ERROR_SUCCESS && resultType == REG_DWORD) {
125 dValue = dValue / 1024 / 1024;
126 destString += OUString::number(int32_t(dValue));
127 } else {
128 retval = false;
130 break;
132 case REG_MULTI_SZ:
134 // A chain of null-separated strings; we convert the nulls to spaces
135 WCHAR wCharValue[1024];
136 dwcbData = sizeof(wCharValue);
138 result = RegQueryValueExW(key, keyName, nullptr, &resultType,
139 (LPBYTE)wCharValue, &dwcbData);
140 if (result == ERROR_SUCCESS && resultType == REG_MULTI_SZ) {
141 // This bit here could probably be cleaner.
142 bool isValid = false;
144 DWORD strLen = dwcbData/sizeof(wCharValue[0]);
145 for (DWORD i = 0; i < strLen; i++) {
146 if (wCharValue[i] == '\0') {
147 if (i < strLen - 1 && wCharValue[i + 1] == '\0') {
148 isValid = true;
149 break;
150 } else {
151 wCharValue[i] = ' ';
156 // ensure wCharValue is null terminated
157 wCharValue[strLen-1] = '\0';
159 if (isValid)
160 destString = OUString(wCharValue);
162 } else {
163 retval = false;
166 break;
169 RegCloseKey(key);
171 return retval;
174 // The driver ID is a string like PCI\VEN_15AD&DEV_0405&SUBSYS_040515AD, possibly
175 // followed by &REV_XXXX. We uppercase the string, and strip the &REV_ part
176 // from it, if found.
177 void normalizeDriverId(OUString& driverid) {
178 driverid = driverid.toAsciiUpperCase();
179 int32_t rev = driverid.indexOf("&REV_");
180 if (rev != -1) {
181 driverid = driverid.copy(0, rev - 1);
185 // The device ID is a string like PCI\VEN_15AD&DEV_0405&SUBSYS_040515AD
186 // this function is used to extract the id's out of it
187 uint32_t ParseIDFromDeviceID(const OUString &key, const char *prefix, int length)
189 OUString id = key.toAsciiUpperCase();
190 OUString aPrefix = OUString::fromUtf8(prefix);
191 int32_t start = id.indexOf(aPrefix);
192 if (start != -1) {
193 id = id.copy(start + aPrefix.getLength(), length);
195 return id.toUInt32(16);
198 // OS version in 16.16 major/minor form
199 // based on http://msdn.microsoft.com/en-us/library/ms724834(VS.85).aspx
200 enum {
201 kWindowsUnknown = 0,
202 kWindowsXP = 0x50001,
203 kWindowsServer2003 = 0x50002,
204 kWindowsVista = 0x60000,
205 kWindows7 = 0x60001,
206 kWindows8 = 0x60002,
207 kWindows8_1 = 0x60003,
208 kWindows10 = 0x60004
212 wgl::OperatingSystem WindowsVersionToOperatingSystem(int32_t aWindowsVersion)
214 switch(aWindowsVersion) {
215 case kWindowsXP:
216 return wgl::DRIVER_OS_WINDOWS_XP;
217 case kWindowsServer2003:
218 return wgl::DRIVER_OS_WINDOWS_SERVER_2003;
219 case kWindowsVista:
220 return wgl::DRIVER_OS_WINDOWS_VISTA;
221 case kWindows7:
222 return wgl::DRIVER_OS_WINDOWS_7;
223 case kWindows8:
224 return wgl::DRIVER_OS_WINDOWS_8;
225 case kWindows8_1:
226 return wgl::DRIVER_OS_WINDOWS_8_1;
227 case kWindowsUnknown:
228 default:
229 return wgl::DRIVER_OS_UNKNOWN;
234 int32_t WindowsOSVersion()
236 static int32_t winVersion = kWindowsUnknown;
238 OSVERSIONINFO vinfo;
240 if (winVersion == kWindowsUnknown) {
241 vinfo.dwOSVersionInfoSize = sizeof (vinfo);
242 #pragma warning(push)
243 #pragma warning(disable:4996)
244 if (!GetVersionEx(&vinfo)) {
245 #pragma warning(pop)
246 winVersion = kWindowsUnknown;
247 } else {
248 winVersion = int32_t(vinfo.dwMajorVersion << 16) + vinfo.dwMinorVersion;
252 return winVersion;
255 // This allows us to pad driver version 'substrings' with 0s, this
256 // effectively allows us to treat the version numbers as 'decimals'. This is
257 // a little strange but this method seems to do the right thing for all
258 // different vendor's driver strings. i.e. .98 will become 9800, which is
259 // larger than .978 which would become 9780.
260 void PadDriverDecimal(char *aString)
262 for (int i = 0; i < 4; i++) {
263 if (!aString[i]) {
264 for (int c = i; c < 4; c++) {
265 aString[c] = '0';
267 break;
270 aString[4] = 0;
273 // All destination string storage needs to have at least 5 bytes available.
274 bool SplitDriverVersion(const char *aSource, char *aAStr, char *aBStr, char *aCStr, char *aDStr)
276 // sscanf doesn't do what we want here to we parse this manually.
277 int len = strlen(aSource);
278 char *dest[4] = { aAStr, aBStr, aCStr, aDStr };
279 unsigned destIdx = 0;
280 unsigned destPos = 0;
282 for (int i = 0; i < len; i++) {
283 if (destIdx > ArrayLength(dest)) {
284 // Invalid format found. Ensure we don't access dest beyond bounds.
285 return false;
288 if (aSource[i] == '.') {
289 dest[destIdx++][destPos] = 0;
290 destPos = 0;
291 continue;
294 if (destPos > 3) {
295 // Ignore more than 4 chars. Ensure we never access dest[destIdx]
296 // beyond its bounds.
297 continue;
300 dest[destIdx][destPos++] = aSource[i];
303 // Add last terminator.
304 dest[destIdx][destPos] = 0;
306 if (destIdx != ArrayLength(dest) - 1) {
307 return false;
309 return true;
312 bool ParseDriverVersion(const OUString& aVersion, uint64_t *aNumericVersion)
314 *aNumericVersion = 0;
316 #if defined(WIN32)
317 int a, b, c, d;
318 char aStr[8], bStr[8], cStr[8], dStr[8];
319 /* honestly, why do I even bother */
320 OString aOVersion = OUStringToOString(aVersion, RTL_TEXTENCODING_UTF8);
321 SAL_WARN("vcl.opengl", "Parse driver ver '" << aOVersion << "'");
322 if (!SplitDriverVersion(aOVersion.getStr(), aStr, bStr, cStr, dStr))
324 #if 0
325 // Intel in their wisdom decided to have 5x digits
326 sal_Int32 nIdx = aOVersion.lastIndexOf( '.' );
327 if (nIdx > 6)
329 OString aAllButLast = aOVersion.copy(0, nIdx);
330 SAL_WARN("vcl.opengl", "Intel five digits cropped to '" << aAllButLast << "'");
331 if (!SplitDriverVersion(aAllButLast.getStr(), aStr, bStr, cStr, dStr))
332 return false;
334 else
335 #endif
336 return false;
339 PadDriverDecimal(bStr);
340 PadDriverDecimal(cStr);
341 PadDriverDecimal(dStr);
343 a = atoi(aStr);
344 b = atoi(bStr);
345 c = atoi(cStr);
346 d = atoi(dStr);
348 if (a < 0 || a > 0xffff) return false;
349 if (b < 0 || b > 0xffff) return false;
350 if (c < 0 || c > 0xffff) return false;
351 if (d < 0 || d > 0xffff) return false;
353 *aNumericVersion = GFX_DRIVER_VERSION(a, b, c, d);
354 return true;
355 #else
356 return false;
357 #endif
359 /* Other interesting places for info:
360 * IDXGIAdapter::GetDesc()
361 * IDirectDraw7::GetAvailableVidMem()
362 * e->GetAvailableTextureMem()
363 * */
365 template<typename T> void appendIntegerWithPadding(OUString& rString, T value, sal_uInt32 nChars)
367 rString += "0x";
368 OUString aValue = OUString::number(value, 16);
369 sal_Int32 nLength = aValue.getLength();
370 sal_uInt32 nPadLength = nChars - nLength;
371 assert(nPadLength >= 0);
372 OUStringBuffer aBuffer;
373 for (sal_uInt32 i = 0; i < nPadLength; ++i)
375 aBuffer.append("0");
377 rString += aBuffer.makeStringAndClear() + aValue;
380 #define DEVICE_KEY_PREFIX L"\\Registry\\Machine\\"
383 namespace wgl {
385 uint64_t DriverInfo::allDriverVersions = ~(uint64_t(0));
386 DriverInfo::DeviceFamilyVector* const DriverInfo::allDevices = nullptr;
388 DriverInfo::DeviceFamilyVector* DriverInfo::mpDeviceFamilies[DeviceFamilyMax];
390 DriverInfo::DriverInfo()
391 : meOperatingSystem(wgl::DRIVER_OS_UNKNOWN),
392 mnOperatingSystemVersion(0),
393 maAdapterVendor(WinOpenGLDeviceInfo::GetDeviceVendor(VendorAll)),
394 mpDevices(allDevices),
395 mbDeleteDevices(false),
396 mbWhitelisted(false),
397 meComparisonOp(DRIVER_COMPARISON_IGNORED),
398 mnDriverVersion(0),
399 mnDriverVersionMax(0)
402 DriverInfo::DriverInfo(OperatingSystem os, const OUString& vendor,
403 DeviceFamilyVector* devices,
404 VersionComparisonOp op,
405 uint64_t driverVersion,
406 bool bWhitelisted,
407 const char *suggestedVersion /* = nullptr */,
408 bool ownDevices /* = false */)
409 : meOperatingSystem(os),
410 mnOperatingSystemVersion(0),
411 maAdapterVendor(vendor),
412 mpDevices(devices),
413 mbDeleteDevices(ownDevices),
414 mbWhitelisted(bWhitelisted),
415 meComparisonOp(op),
416 mnDriverVersion(driverVersion),
417 mnDriverVersionMax(0)
419 if (suggestedVersion)
420 maSuggestedVersion = OStringToOUString(OString(suggestedVersion), RTL_TEXTENCODING_UTF8);
423 DriverInfo::DriverInfo(const DriverInfo& aOrig)
424 : meOperatingSystem(aOrig.meOperatingSystem),
425 mnOperatingSystemVersion(aOrig.mnOperatingSystemVersion),
426 maAdapterVendor(aOrig.maAdapterVendor),
427 mbWhitelisted(aOrig.mbWhitelisted),
428 meComparisonOp(aOrig.meComparisonOp),
429 mnDriverVersion(aOrig.mnDriverVersion),
430 mnDriverVersionMax(aOrig.mnDriverVersionMax)
432 //If we're managing the lifetime of the device family, we have to make a
433 // copy of the original's device family.
434 if (aOrig.mbDeleteDevices && aOrig.mpDevices) {
435 mpDevices = new DeviceFamilyVector;
436 *mpDevices = *aOrig.mpDevices;
437 } else {
438 mpDevices = aOrig.mpDevices;
441 mbDeleteDevices = aOrig.mbDeleteDevices;
444 DriverInfo::~DriverInfo()
446 if (mbDeleteDevices)
447 delete mpDevices;
450 // Macros for appending a device to the DeviceFamily.
451 #define APPEND_DEVICE(device) APPEND_DEVICE2(#device)
452 #define APPEND_DEVICE2(device) deviceFamily->push_back(OUString("#device"))
454 const DriverInfo::DeviceFamilyVector* DriverInfo::GetDeviceFamily(DeviceFamily id)
456 // The code here is too sensitive to fall through to the default case if the
457 // code is invalid.
458 assert(id >= 0 && id < DeviceFamilyMax);
460 // If it already exists, we must have processed it once, so return it now.
461 if (mpDeviceFamilies[id])
462 return mpDeviceFamilies[id];
464 mpDeviceFamilies[id] = new wgl::DriverInfo::DeviceFamilyVector;
465 wgl::DriverInfo::DeviceFamilyVector* deviceFamily = mpDeviceFamilies[id];
467 switch (id) {
468 case IntelGMA500:
469 APPEND_DEVICE(0x8108); /* IntelGMA500_1 */
470 APPEND_DEVICE(0x8109); /* IntelGMA500_2 */
471 break;
472 case IntelGMA900:
473 APPEND_DEVICE(0x2582); /* IntelGMA900_1 */
474 APPEND_DEVICE(0x2782); /* IntelGMA900_2 */
475 APPEND_DEVICE(0x2592); /* IntelGMA900_3 */
476 APPEND_DEVICE(0x2792); /* IntelGMA900_4 */
477 break;
478 case IntelGMA950:
479 APPEND_DEVICE(0x2772); /* Intel945G_1 */
480 APPEND_DEVICE(0x2776); /* Intel945G_2 */
481 APPEND_DEVICE(0x27a2); /* Intel945_1 */
482 APPEND_DEVICE(0x27a6); /* Intel945_2 */
483 APPEND_DEVICE(0x27ae); /* Intel945_3 */
484 break;
485 case IntelGMA3150:
486 APPEND_DEVICE(0xa001); /* IntelGMA3150_Nettop_1 */
487 APPEND_DEVICE(0xa002); /* IntelGMA3150_Nettop_2 */
488 APPEND_DEVICE(0xa011); /* IntelGMA3150_Netbook_1 */
489 APPEND_DEVICE(0xa012); /* IntelGMA3150_Netbook_2 */
490 break;
491 case IntelGMAX3000:
492 APPEND_DEVICE(0x2972); /* Intel946GZ_1 */
493 APPEND_DEVICE(0x2973); /* Intel946GZ_2 */
494 APPEND_DEVICE(0x2982); /* IntelG35_1 */
495 APPEND_DEVICE(0x2983); /* IntelG35_2 */
496 APPEND_DEVICE(0x2992); /* IntelQ965_1 */
497 APPEND_DEVICE(0x2993); /* IntelQ965_2 */
498 APPEND_DEVICE(0x29a2); /* IntelG965_1 */
499 APPEND_DEVICE(0x29a3); /* IntelG965_2 */
500 APPEND_DEVICE(0x29b2); /* IntelQ35_1 */
501 APPEND_DEVICE(0x29b3); /* IntelQ35_2 */
502 APPEND_DEVICE(0x29c2); /* IntelG33_1 */
503 APPEND_DEVICE(0x29c3); /* IntelG33_2 */
504 APPEND_DEVICE(0x29d2); /* IntelQ33_1 */
505 APPEND_DEVICE(0x29d3); /* IntelQ33_2 */
506 APPEND_DEVICE(0x2a02); /* IntelGL960_1 */
507 APPEND_DEVICE(0x2a03); /* IntelGL960_2 */
508 APPEND_DEVICE(0x2a12); /* IntelGM965_1 */
509 APPEND_DEVICE(0x2a13); /* IntelGM965_2 */
510 break;
511 case IntelGMAX4500HD:
512 APPEND_DEVICE(0x2a42); /* IntelGMA4500MHD_1 */
513 APPEND_DEVICE(0x2a43); /* IntelGMA4500MHD_2 */
514 APPEND_DEVICE(0x2e42); /* IntelB43_1 */
515 APPEND_DEVICE(0x2e43); /* IntelB43_2 */
516 APPEND_DEVICE(0x2e92); /* IntelB43_3 */
517 APPEND_DEVICE(0x2e93); /* IntelB43_4 */
518 APPEND_DEVICE(0x2e32); /* IntelG41_1 */
519 APPEND_DEVICE(0x2e33); /* IntelG41_2 */
520 APPEND_DEVICE(0x2e22); /* IntelG45_1 */
521 APPEND_DEVICE(0x2e23); /* IntelG45_2 */
522 APPEND_DEVICE(0x2e12); /* IntelQ45_1 */
523 APPEND_DEVICE(0x2e13); /* IntelQ45_2 */
524 APPEND_DEVICE(0x0042); /* IntelHDGraphics */
525 APPEND_DEVICE(0x0046); /* IntelMobileHDGraphics */
526 APPEND_DEVICE(0x0102); /* IntelSandyBridge_1 */
527 APPEND_DEVICE(0x0106); /* IntelSandyBridge_2 */
528 APPEND_DEVICE(0x0112); /* IntelSandyBridge_3 */
529 APPEND_DEVICE(0x0116); /* IntelSandyBridge_4 */
530 APPEND_DEVICE(0x0122); /* IntelSandyBridge_5 */
531 APPEND_DEVICE(0x0126); /* IntelSandyBridge_6 */
532 APPEND_DEVICE(0x010a); /* IntelSandyBridge_7 */
533 APPEND_DEVICE(0x0080); /* IntelIvyBridge */
534 break;
535 case IntelHD3000:
536 APPEND_DEVICE(0x0126);
537 break;
538 case IntelMobileHDGraphics:
539 APPEND_DEVICE(0x0046); /* IntelMobileHDGraphics */
540 break;
541 case NvidiaBlockD3D9Layers:
542 // Glitches whilst scrolling (see bugs 612007, 644787, 645872)
543 APPEND_DEVICE(0x00f3); /* NV43 [GeForce 6200 (TM)] */
544 APPEND_DEVICE(0x0146); /* NV43 [Geforce Go 6600TE/6200TE (TM)] */
545 APPEND_DEVICE(0x014f); /* NV43 [GeForce 6200 (TM)] */
546 APPEND_DEVICE(0x0161); /* NV44 [GeForce 6200 TurboCache (TM)] */
547 APPEND_DEVICE(0x0162); /* NV44 [GeForce 6200SE TurboCache (TM)] */
548 APPEND_DEVICE(0x0163); /* NV44 [GeForce 6200 LE (TM)] */
549 APPEND_DEVICE(0x0164); /* NV44 [GeForce Go 6200 (TM)] */
550 APPEND_DEVICE(0x0167); /* NV43 [GeForce Go 6200/6400 (TM)] */
551 APPEND_DEVICE(0x0168); /* NV43 [GeForce Go 6200/6400 (TM)] */
552 APPEND_DEVICE(0x0169); /* NV44 [GeForce 6250 (TM)] */
553 APPEND_DEVICE(0x0222); /* NV44 [GeForce 6200 A-LE (TM)] */
554 APPEND_DEVICE(0x0240); /* C51PV [GeForce 6150 (TM)] */
555 APPEND_DEVICE(0x0241); /* C51 [GeForce 6150 LE (TM)] */
556 APPEND_DEVICE(0x0244); /* C51 [Geforce Go 6150 (TM)] */
557 APPEND_DEVICE(0x0245); /* C51 [Quadro NVS 210S/GeForce 6150LE (TM)] */
558 APPEND_DEVICE(0x0247); /* C51 [GeForce Go 6100 (TM)] */
559 APPEND_DEVICE(0x03d0); /* C61 [GeForce 6150SE nForce 430 (TM)] */
560 APPEND_DEVICE(0x03d1); /* C61 [GeForce 6100 nForce 405 (TM)] */
561 APPEND_DEVICE(0x03d2); /* C61 [GeForce 6100 nForce 400 (TM)] */
562 APPEND_DEVICE(0x03d5); /* C61 [GeForce 6100 nForce 420 (TM)] */
563 break;
564 case RadeonX1000:
565 // This list is from the ATIRadeonX1000.kext Info.plist
566 APPEND_DEVICE(0x7187);
567 APPEND_DEVICE(0x7210);
568 APPEND_DEVICE(0x71de);
569 APPEND_DEVICE(0x7146);
570 APPEND_DEVICE(0x7142);
571 APPEND_DEVICE(0x7109);
572 APPEND_DEVICE(0x71c5);
573 APPEND_DEVICE(0x71c0);
574 APPEND_DEVICE(0x7240);
575 APPEND_DEVICE(0x7249);
576 APPEND_DEVICE(0x7291);
577 break;
578 case Geforce7300GT:
579 APPEND_DEVICE(0x0393);
580 break;
581 case Nvidia310M:
582 APPEND_DEVICE(0x0A70);
583 break;
584 // This should never happen, but we get a warning if we don't handle this.
585 case DeviceFamilyMax:
586 SAL_WARN("vcl.opengl", "Invalid DeviceFamily id");
587 break;
590 return deviceFamily;
595 WinOpenGLDeviceInfo::WinOpenGLDeviceInfo():
596 mbHasDualGPU(false),
597 mbHasDriverVersionMismatch(false),
598 mbRDP(false)
600 GetData();
601 FillBlacklist();
604 WinOpenGLDeviceInfo::~WinOpenGLDeviceInfo()
608 bool WinOpenGLDeviceInfo::FindBlocklistedDeviceInList()
610 uint64_t driverVersion;
611 ParseDriverVersion(maDriverVersion, &driverVersion);
613 wgl::OperatingSystem eOS = WindowsVersionToOperatingSystem(mnWindowsVersion);
615 if (eOS < wgl::DRIVER_OS_WINDOWS_7 &&
616 eOS != wgl::DRIVER_OS_UNKNOWN /* the future */)
618 SAL_WARN("vcl.opengl", "All Windows < Windows 7 black-listed for OpenGL");
619 return true;
622 bool match = false;
623 uint32_t i = 0;
624 for (; i < maDriverInfo.size(); i++) {
625 if (maDriverInfo[i].meOperatingSystem != wgl::DRIVER_OS_ALL &&
626 maDriverInfo[i].meOperatingSystem != eOS)
628 continue;
631 if (maDriverInfo[i].mnOperatingSystemVersion && maDriverInfo[i].mnOperatingSystemVersion != mnWindowsVersion) {
632 continue;
635 if (!maDriverInfo[i].maAdapterVendor.equalsIgnoreAsciiCase(GetDeviceVendor(wgl::VendorAll)) &&
636 !maDriverInfo[i].maAdapterVendor.equalsIgnoreAsciiCase(maAdapterVendorID)) {
637 continue;
640 if (maDriverInfo[i].mpDevices != wgl::DriverInfo::allDevices && maDriverInfo[i].mpDevices->size()) {
641 bool deviceMatches = false;
642 for (uint32_t j = 0; j < maDriverInfo[i].mpDevices->size(); j++) {
643 if ((*maDriverInfo[i].mpDevices)[j].equalsIgnoreAsciiCase(maAdapterDeviceID)) {
644 deviceMatches = true;
645 break;
649 if (!deviceMatches) {
650 continue;
654 switch (maDriverInfo[i].meComparisonOp) {
655 case wgl::DRIVER_LESS_THAN:
656 match = driverVersion < maDriverInfo[i].mnDriverVersion;
657 break;
658 case wgl::DRIVER_LESS_THAN_OR_EQUAL:
659 match = driverVersion <= maDriverInfo[i].mnDriverVersion;
660 break;
661 case wgl::DRIVER_GREATER_THAN:
662 match = driverVersion > maDriverInfo[i].mnDriverVersion;
663 break;
664 case wgl::DRIVER_GREATER_THAN_OR_EQUAL:
665 match = driverVersion >= maDriverInfo[i].mnDriverVersion;
666 break;
667 case wgl::DRIVER_EQUAL:
668 match = driverVersion == maDriverInfo[i].mnDriverVersion;
669 break;
670 case wgl::DRIVER_NOT_EQUAL:
671 match = driverVersion != maDriverInfo[i].mnDriverVersion;
672 break;
673 case wgl::DRIVER_BETWEEN_EXCLUSIVE:
674 match = driverVersion > maDriverInfo[i].mnDriverVersion && driverVersion < maDriverInfo[i].mnDriverVersionMax;
675 break;
676 case wgl::DRIVER_BETWEEN_INCLUSIVE:
677 match = driverVersion >= maDriverInfo[i].mnDriverVersion && driverVersion <= maDriverInfo[i].mnDriverVersionMax;
678 break;
679 case wgl::DRIVER_BETWEEN_INCLUSIVE_START:
680 match = driverVersion >= maDriverInfo[i].mnDriverVersion && driverVersion < maDriverInfo[i].mnDriverVersionMax;
681 break;
682 case wgl::DRIVER_COMPARISON_IGNORED:
683 // We don't have a comparison op, so we match everything.
684 match = true;
685 break;
686 default:
687 SAL_WARN("vcl.opengl", "Bogus op in GfxDriverInfo");
688 break;
691 if (match || maDriverInfo[i].mnDriverVersion == wgl::DriverInfo::allDriverVersions) {
692 // white listed drivers
693 if (maDriverInfo[i].mbWhitelisted)
695 SAL_WARN("vcl.opengl", "whitelisted driver");
696 return false;
699 match = true;
700 SAL_WARN("vcl.opengl", "use : " << maDriverInfo[i].maSuggestedVersion);
701 break;
705 return match;
708 bool WinOpenGLDeviceInfo::isDeviceBlocked()
710 SAL_INFO("vcl.opengl", maDriverVersion);
711 SAL_INFO("vcl.opengl", maDriverDate);
712 SAL_INFO("vcl.opengl", maDeviceID);
713 SAL_INFO("vcl.opengl", maAdapterVendorID);
714 SAL_INFO("vcl.opengl", maAdapterDeviceID);
715 SAL_INFO("vcl.opengl", maAdapterSubsysID);
716 SAL_INFO("vcl.opengl", maDeviceKey);
717 SAL_INFO("vcl.opengl", maDeviceString);
719 // Check if the device is blocked from the downloaded blocklist. If not, check
720 // the static list after that. This order is used so that we can later escape
721 // out of static blocks (i.e. if we were wrong or something was patched, we
722 // can back out our static block without doing a release).
724 if (mbRDP)
726 SAL_WARN("vcl.opengl", "all OpenGL blocked for RDP sessions");
727 return true;
730 /* Anything that's exotic eg. VMWare / VirtualBox GL drivers
731 we're not interested in for now. */
732 if (maAdapterVendorID != GetDeviceVendor(wgl::VendorAMD) &&
733 maAdapterVendorID != GetDeviceVendor(wgl::VendorATI) &&
734 maAdapterVendorID != GetDeviceVendor(wgl::VendorIntel) &&
735 maAdapterVendorID != GetDeviceVendor(wgl::VendorNVIDIA))
736 return true;
738 return FindBlocklistedDeviceInList();
741 void WinOpenGLDeviceInfo::GetData()
743 DISPLAY_DEVICEW displayDevice;
744 displayDevice.cb = sizeof(displayDevice);
746 mnWindowsVersion = WindowsOSVersion();
747 int deviceIndex = 0;
749 while (EnumDisplayDevicesW(nullptr, deviceIndex, &displayDevice, 0)) {
750 if (displayDevice.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) {
751 break;
753 deviceIndex++;
756 // make sure the string is nullptr terminated
757 if (wcsnlen(displayDevice.DeviceKey, ArrayLength(displayDevice.DeviceKey))
758 == ArrayLength(displayDevice.DeviceKey)) {
759 // we did not find a nullptr
760 SAL_WARN("vcl.opengl", "no null pointer");
761 return;
764 /* DeviceKey is "reserved" according to MSDN so we'll be careful with it */
765 /* check that DeviceKey begins with DEVICE_KEY_PREFIX */
766 /* some systems have a DeviceKey starting with \REGISTRY\Machine\ so we need to compare case insenstively */
767 if (_wcsnicmp(displayDevice.DeviceKey, DEVICE_KEY_PREFIX, ArrayLength(DEVICE_KEY_PREFIX)-1) != 0)
769 SAL_WARN("vcl.opengl", "incorrect DeviceKey");
770 return;
773 // chop off DEVICE_KEY_PREFIX
774 maDeviceKey = displayDevice.DeviceKey + ArrayLength(DEVICE_KEY_PREFIX)-1;
776 maDeviceID = displayDevice.DeviceID;
777 maDeviceString = displayDevice.DeviceString;
779 if (maDeviceID.isEmpty() &&
780 maDeviceString == "RDPUDD Chained DD")
782 // TODO: moggi: we need to block RDP as it does not provide OpenGL 2.1+
783 mbRDP = true;
784 SAL_WARN("vcl.opengl", "RDP => blocked");
785 return;
788 /* create a device information set composed of the current display device */
789 HDEVINFO devinfo = SetupDiGetClassDevsW(nullptr, maDeviceID.getStr(), nullptr,
790 DIGCF_PRESENT | DIGCF_PROFILE | DIGCF_ALLCLASSES);
792 if (devinfo != INVALID_HANDLE_VALUE) {
793 HKEY key;
794 LONG result;
795 WCHAR value[255];
796 DWORD dwcbData;
797 SP_DEVINFO_DATA devinfoData;
798 DWORD memberIndex = 0;
800 devinfoData.cbSize = sizeof(devinfoData);
801 OUString aDriverKeyPre("System\\CurrentControlSet\\Control\\Class\\");
802 /* enumerate device information elements in the device information set */
803 while (SetupDiEnumDeviceInfo(devinfo, memberIndex++, &devinfoData)) {
804 /* get a string that identifies the device's driver key */
805 if (SetupDiGetDeviceRegistryPropertyW(devinfo,
806 &devinfoData,
807 SPDRP_DRIVER,
808 nullptr,
809 (PBYTE)value,
810 sizeof(value),
811 nullptr)) {
812 OUString driverKey(aDriverKeyPre);
813 driverKey += value;
814 result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, driverKey.getStr(), 0, KEY_QUERY_VALUE, &key);
815 if (result == ERROR_SUCCESS) {
816 /* we've found the driver we're looking for */
817 dwcbData = sizeof(value);
818 result = RegQueryValueExW(key, L"DriverVersion", nullptr, nullptr,
819 (LPBYTE)value, &dwcbData);
820 if (result == ERROR_SUCCESS) {
821 maDriverVersion = OUString(value);
822 } else {
823 // If the entry wasn't found, assume the worst (0.0.0.0).
824 maDriverVersion = OUString("0.0.0.0");
826 dwcbData = sizeof(value);
827 result = RegQueryValueExW(key, L"DriverDate", nullptr, nullptr,
828 (LPBYTE)value, &dwcbData);
829 if (result == ERROR_SUCCESS) {
830 maDriverDate = value;
831 } else {
832 // Again, assume the worst
833 maDriverDate = OUString("01-01-1970");
835 RegCloseKey(key);
836 break;
841 SetupDiDestroyDeviceInfoList(devinfo);
843 else
845 SAL_WARN("vcl.opengl", "invalid handle value");
848 appendIntegerWithPadding(maAdapterVendorID, ParseIDFromDeviceID(maDeviceID, "VEN_", 4), 4);
849 appendIntegerWithPadding(maAdapterDeviceID, ParseIDFromDeviceID(maDeviceID, "&DEV_", 4), 4);
850 appendIntegerWithPadding(maAdapterSubsysID, ParseIDFromDeviceID(maDeviceID, "&SUBSYS_", 8), 8);
852 // We now check for second display adapter.
854 // Device interface class for display adapters.
855 CLSID GUID_DISPLAY_DEVICE_ARRIVAL;
856 HRESULT hresult = CLSIDFromString(L"{1CA05180-A699-450A-9A0C-DE4FBE3DDD89}",
857 &GUID_DISPLAY_DEVICE_ARRIVAL);
858 if (hresult == NOERROR) {
859 devinfo = SetupDiGetClassDevsW(&GUID_DISPLAY_DEVICE_ARRIVAL,
860 nullptr, nullptr,
861 DIGCF_PRESENT | DIGCF_INTERFACEDEVICE);
863 if (devinfo != INVALID_HANDLE_VALUE) {
864 HKEY key;
865 LONG result;
866 WCHAR value[255];
867 DWORD dwcbData;
868 SP_DEVINFO_DATA devinfoData;
869 DWORD memberIndex = 0;
870 devinfoData.cbSize = sizeof(devinfoData);
872 OUString aAdapterDriver2;
873 OUString aDeviceID2;
874 OUString aDriverVersion2;
875 OUString aDriverDate2;
876 uint32_t adapterVendorID2;
877 uint32_t adapterDeviceID2;
879 OUString aDriverKeyPre("System\\CurrentControlSet\\Control\\Class\\");
880 /* enumerate device information elements in the device information set */
881 while (SetupDiEnumDeviceInfo(devinfo, memberIndex++, &devinfoData)) {
882 /* get a string that identifies the device's driver key */
883 if (SetupDiGetDeviceRegistryPropertyW(devinfo,
884 &devinfoData,
885 SPDRP_DRIVER,
886 nullptr,
887 (PBYTE)value,
888 sizeof(value),
889 nullptr)) {
890 OUString driverKey2(aDriverKeyPre);
891 driverKey2 += value;
892 result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, driverKey2.getStr(), 0, KEY_QUERY_VALUE, &key);
893 if (result == ERROR_SUCCESS) {
894 dwcbData = sizeof(value);
895 result = RegQueryValueExW(key, L"MatchingDeviceId", nullptr,
896 nullptr, (LPBYTE)value, &dwcbData);
897 if (result != ERROR_SUCCESS) {
898 continue;
900 aDeviceID2 = value;
901 OUString aAdapterVendorID2String;
902 OUString aAdapterDeviceID2String;
903 adapterVendorID2 = ParseIDFromDeviceID(aDeviceID2, "VEN_", 4);
904 appendIntegerWithPadding(aAdapterVendorID2String, adapterVendorID2, 4);
905 adapterDeviceID2 = ParseIDFromDeviceID(aDeviceID2, "&DEV_", 4);
906 appendIntegerWithPadding(aAdapterDeviceID2String, adapterDeviceID2, 4);
907 if (maAdapterVendorID == aAdapterVendorID2String &&
908 maAdapterDeviceID == aAdapterDeviceID2String) {
909 RegCloseKey(key);
910 continue;
913 // If this device is missing driver information, it is unlikely to
914 // be a real display adapter.
915 if (!GetKeyValue(driverKey2.getStr(), L"InstalledDisplayDrivers",
916 aAdapterDriver2, REG_MULTI_SZ)) {
917 RegCloseKey(key);
918 continue;
920 dwcbData = sizeof(value);
921 result = RegQueryValueExW(key, L"DriverVersion", nullptr, nullptr,
922 (LPBYTE)value, &dwcbData);
923 if (result != ERROR_SUCCESS) {
924 RegCloseKey(key);
925 continue;
927 aDriverVersion2 = value;
928 dwcbData = sizeof(value);
929 result = RegQueryValueExW(key, L"DriverDate", nullptr, nullptr,
930 (LPBYTE)value, &dwcbData);
931 if (result != ERROR_SUCCESS) {
932 RegCloseKey(key);
933 continue;
935 aDriverDate2 = value;
936 dwcbData = sizeof(value);
937 result = RegQueryValueExW(key, L"Device Description", nullptr,
938 nullptr, (LPBYTE)value, &dwcbData);
939 if (result != ERROR_SUCCESS) {
940 dwcbData = sizeof(value);
941 result = RegQueryValueExW(key, L"DriverDesc", nullptr, nullptr,
942 (LPBYTE)value, &dwcbData);
944 RegCloseKey(key);
945 if (result == ERROR_SUCCESS) {
946 mbHasDualGPU = true;
947 maDeviceString2 = value;
948 maDeviceID2 = aDeviceID2;
949 maDeviceKey2 = driverKey2;
950 maDriverVersion2 = aDriverVersion2;
951 maDriverDate2 = aDriverDate2;
952 appendIntegerWithPadding(maAdapterVendorID2, adapterVendorID2, 4);
953 appendIntegerWithPadding(maAdapterDeviceID2, adapterDeviceID2, 4);
954 appendIntegerWithPadding(maAdapterSubsysID2, ParseIDFromDeviceID(maDeviceID2, "&SUBSYS_", 8), 8);
955 break;
961 SetupDiDestroyDeviceInfoList(devinfo);
965 mbHasDriverVersionMismatch = false;
966 if (maAdapterVendorID == GetDeviceVendor(wgl::VendorIntel)) {
967 // we've had big crashers (bugs 590373 and 595364) apparently correlated
968 // with bad Intel driver installations where the DriverVersion reported
969 // by the registry was not the version of the DLL.
970 OUString aDLLFileName("igd10umd32.dll");
971 OUString aDLLFileName2("igd10iumd32.dll");
972 OUString aDLLVersion, aDLLVersion2;
973 GetDLLVersion(aDLLFileName.getStr(), aDLLVersion);
974 GetDLLVersion(aDLLFileName2.getStr(), aDLLVersion2);
976 uint64_t dllNumericVersion = 0, dllNumericVersion2 = 0,
977 driverNumericVersion = 0, knownSafeMismatchVersion = 0;
978 ParseDriverVersion(aDLLVersion, &dllNumericVersion);
979 ParseDriverVersion(aDLLVersion2, &dllNumericVersion2);
980 ParseDriverVersion(maDriverVersion, &driverNumericVersion);
981 ParseDriverVersion("9.17.10.0", &knownSafeMismatchVersion);
983 // If there's a driver version mismatch, consider this harmful only when
984 // the driver version is less than knownSafeMismatchVersion. See the
985 // above comment about crashes with old mismatches. If the GetDllVersion
986 // call fails, then they return 0, so that will be considered a mismatch.
987 if (dllNumericVersion != driverNumericVersion &&
988 dllNumericVersion2 != driverNumericVersion &&
989 (driverNumericVersion < knownSafeMismatchVersion ||
990 std::max(dllNumericVersion, dllNumericVersion2) < knownSafeMismatchVersion)) {
991 mbHasDriverVersionMismatch = true;
998 // Macro for assigning a device vendor id to a string.
999 #define DECLARE_VENDOR_ID(name, deviceId) \
1000 case name: \
1001 *mpDeviceVendors[id] = OUString(deviceId); \
1002 break;
1004 OUString WinOpenGLDeviceInfo::GetDeviceVendor(wgl::DeviceVendor id)
1006 assert(id >= 0 && id < wgl::DeviceVendorMax);
1008 if (mpDeviceVendors[id])
1009 return *mpDeviceVendors[id];
1011 mpDeviceVendors[id] = new OUString();
1013 switch (id) {
1014 DECLARE_VENDOR_ID(wgl::VendorAll, "");
1015 DECLARE_VENDOR_ID(wgl::VendorIntel, "0x8086");
1016 DECLARE_VENDOR_ID(wgl::VendorNVIDIA, "0x10de");
1017 DECLARE_VENDOR_ID(wgl::VendorAMD, "0x1022");
1018 DECLARE_VENDOR_ID(wgl::VendorATI, "0x1002");
1019 DECLARE_VENDOR_ID(wgl::VendorMicrosoft, "0x1414");
1020 // Suppress a warning.
1021 DECLARE_VENDOR_ID(wgl::DeviceVendorMax, "");
1024 return *mpDeviceVendors[id];
1027 void WinOpenGLDeviceInfo::FillBlacklist()
1030 * Implement whitelist entries first as they will be used first to stop early;
1032 // APPEND_TO_DRIVER_WHITELIST( wgl::DRIVER_OS_WINDOWS_7, GetDeviceVendor(wgl::VendorIntel),
1033 // wgl::DriverInfo::allDevices, wgl::DRIVER_EQUAL, wgl::V(10,18,10,3412));
1035 * It should be noted here that more specialized rules on certain features
1036 * should be inserted -before- more generalized restriction. As the first
1037 * match for feature/OS/device found in the list will be used for the final
1038 * blacklisting call.
1041 /* For blocking obsolete hardware by device family */
1042 #define IMPLEMENT_BLOCK_DEVICE(devFamily) \
1043 APPEND_TO_DRIVER_BLOCKLIST2( wgl::DRIVER_OS_ALL, \
1044 GetDeviceVendor(wgl::VendorIntel), \
1045 (wgl::DriverInfo::DeviceFamilyVector*) wgl::DriverInfo::GetDeviceFamily(devFamily), \
1046 wgl::DRIVER_COMPARISON_IGNORED, wgl::V(0,0,0,0) )
1049 * NVIDIA entries
1051 APPEND_TO_DRIVER_BLOCKLIST( wgl::DRIVER_OS_ALL,
1052 GetDeviceVendor(wgl::VendorNVIDIA), wgl::DriverInfo::allDevices,
1053 wgl::DRIVER_LESS_THAN, wgl::V(10,18,13,5362), "353.62" );
1054 IMPLEMENT_BLOCK_DEVICE(wgl::Geforce7300GT);
1055 IMPLEMENT_BLOCK_DEVICE(wgl::Nvidia310M);
1056 IMPLEMENT_BLOCK_DEVICE(wgl::NvidiaBlockD3D9Layers);
1059 * AMD/ATI entries
1061 APPEND_TO_DRIVER_BLOCKLIST( wgl::DRIVER_OS_ALL,
1062 GetDeviceVendor(wgl::VendorATI), wgl::DriverInfo::allDevices,
1063 wgl::DRIVER_LESS_THAN, wgl::V(15,200,1062,1004), "15.200" );
1064 APPEND_TO_DRIVER_BLOCKLIST( wgl::DRIVER_OS_ALL,
1065 GetDeviceVendor(wgl::VendorAMD), wgl::DriverInfo::allDevices,
1066 wgl::DRIVER_LESS_THAN, wgl::V(15,200,1062,1004), "15.200" );
1067 IMPLEMENT_BLOCK_DEVICE(wgl::RadeonX1000);
1070 * Intel entries
1072 APPEND_TO_DRIVER_BLOCKLIST( wgl::DRIVER_OS_ALL, GetDeviceVendor(wgl::VendorIntel),
1073 wgl::DriverInfo::allDevices, wgl::DRIVER_LESS_THAN,
1074 wgl::V(15,40,4,64), "15.40.4.64.4256");
1076 IMPLEMENT_BLOCK_DEVICE(wgl::IntelGMA500);
1077 IMPLEMENT_BLOCK_DEVICE(wgl::IntelGMA900);
1078 IMPLEMENT_BLOCK_DEVICE(wgl::IntelGMA950);
1079 IMPLEMENT_BLOCK_DEVICE(wgl::IntelGMA3150);
1080 IMPLEMENT_BLOCK_DEVICE(wgl::IntelGMAX3000);
1081 IMPLEMENT_BLOCK_DEVICE(wgl::IntelGMAX4500HD);
1082 IMPLEMENT_BLOCK_DEVICE(wgl::IntelHD3000);
1084 /* Microsoft RemoteFX; blocked less than 6.2.0.0 */
1085 APPEND_TO_DRIVER_BLOCKLIST( wgl::DRIVER_OS_ALL,
1086 GetDeviceVendor(wgl::VendorMicrosoft), wgl::DriverInfo::allDevices,
1087 wgl::DRIVER_LESS_THAN, wgl::V(6,2,0,0), "< 6.2.0.0" );
1091 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */