Add support for GPU bug list reading.
- Add support for reading out feature lists from GPU bug lists in Chromium format (supporting OS + version, exceptions lists, device ids and vendor ids). Add a overloads allowing for passing file name, data or JSON documents. - Test reading in tst_qopenglconfig - Prototypically have the Windows plugin read the file from the environment variable "QT_OPENGL_BUGLIST" and turn off renderers depending on the standard keyword "disable_d3d11" and newly introduced keywords "disable_d3d9", "disable_desktopgl". - QT_OPENGL_BUGLIST can point to an absolute or relative path, in which case it is resolved via QLibraryInfo::SettingsPath and QStandardPaths::ConfigLocation. [ChangeLog][QtGui][Windows] Introduce experimental feature allowing the user to specify a GPU driver buglist with some additional keywords to chooose the GL renderer backend depending on GPU. Task-number: QTBUG-43263 Change-Id: I8c20b0e8ee3cf575b91d0672bf405c42d3e9fb64 Reviewed-by: Laszlo Agocs <laszlo.agocs@theqtcompany.com>bb10
parent
af495c2042
commit
4dd7aeee10
|
|
@ -37,6 +37,15 @@
|
|||
#include "qopenglcontext.h"
|
||||
#include "qopenglfunctions.h"
|
||||
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QJsonDocument>
|
||||
#include <QtCore/QJsonValue>
|
||||
#include <QtCore/QJsonObject>
|
||||
#include <QtCore/QJsonArray>
|
||||
#include <QtCore/QTextStream>
|
||||
#include <QtCore/QFile>
|
||||
#include <QtCore/QDir>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
#if defined(QT_OPENGL_3)
|
||||
|
|
@ -79,4 +88,364 @@ QOpenGLExtensionMatcher::QOpenGLExtensionMatcher()
|
|||
}
|
||||
}
|
||||
|
||||
/* Helpers to read out the list of features matching a device from
|
||||
* a Chromium driver bug list of the format using a subset of keys
|
||||
* (namely, matching by gl_vendor RegExp is not implemented):
|
||||
{
|
||||
"entries": [
|
||||
{
|
||||
"id": 20,
|
||||
"description": "Disable EXT_draw_buffers on GeForce GT 650M on Linux due to driver bugs",
|
||||
"os": {
|
||||
"type": "linux"
|
||||
},
|
||||
// Optional: "exceptions" list
|
||||
"vendor_id": "0x10de",
|
||||
"device_id": ["0x0fd5"],
|
||||
"multi_gpu_category": "any",
|
||||
"features": [
|
||||
"disable_ext_draw_buffers"
|
||||
]
|
||||
},
|
||||
....
|
||||
}
|
||||
*/
|
||||
|
||||
QDebug operator<<(QDebug d, const QOpenGLConfig::Gpu &g)
|
||||
{
|
||||
QDebugStateSaver s(d);
|
||||
d.nospace();
|
||||
d << "Gpu(";
|
||||
if (g.isValid()) {
|
||||
d << "vendor=" << hex << showbase <<g.vendorId << ", device=" << g.deviceId
|
||||
<< "version=" << g.driverVersion;
|
||||
} else {
|
||||
d << 0;
|
||||
}
|
||||
d << ')';
|
||||
return d;
|
||||
}
|
||||
|
||||
enum Operator { NotEqual, LessThan, LessEqualThan, Equals, GreaterThan, GreaterEqualThan };
|
||||
static const char *operators[] = {"!=", "<", "<=", "=", ">", ">="};
|
||||
|
||||
static inline QString valueKey() { return QStringLiteral("value"); }
|
||||
static inline QString opKey() { return QStringLiteral("op"); }
|
||||
static inline QString versionKey() { return QStringLiteral("version"); }
|
||||
static inline QString typeKey() { return QStringLiteral("type"); }
|
||||
static inline QString osKey() { return QStringLiteral("os"); }
|
||||
static inline QString vendorIdKey() { return QStringLiteral("vendor_id"); }
|
||||
static inline QString glVendorKey() { return QStringLiteral("gl_vendor"); }
|
||||
static inline QString deviceIdKey() { return QStringLiteral("device_id"); }
|
||||
static inline QString driverVersionKey() { return QStringLiteral("driver_version"); }
|
||||
static inline QString featuresKey() { return QStringLiteral("features"); }
|
||||
static inline QString idKey() { return QStringLiteral("id"); }
|
||||
static inline QString descriptionKey() { return QStringLiteral("description"); }
|
||||
static inline QString exceptionsKey() { return QStringLiteral("exceptions"); }
|
||||
|
||||
namespace {
|
||||
// VersionTerm describing a version term consisting of number and operator
|
||||
// found in "os", "driver_version", "gl_version".
|
||||
struct VersionTerm {
|
||||
VersionTerm() : op(NotEqual) {}
|
||||
static VersionTerm fromJson(const QJsonValue &v);
|
||||
bool isNull() const { return number.isNull(); }
|
||||
bool matches(const QVersionNumber &other) const;
|
||||
|
||||
QVersionNumber number;
|
||||
Operator op;
|
||||
};
|
||||
|
||||
bool VersionTerm::matches(const QVersionNumber &other) const
|
||||
{
|
||||
if (isNull() || other.isNull()) {
|
||||
qWarning() << Q_FUNC_INFO << "called with invalid parameters";
|
||||
return false;
|
||||
}
|
||||
switch (op) {
|
||||
case NotEqual:
|
||||
return other != number;
|
||||
case LessThan:
|
||||
return other < number;
|
||||
case LessEqualThan:
|
||||
return other <= number;
|
||||
case Equals:
|
||||
return other == number;
|
||||
case GreaterThan:
|
||||
return other > number;
|
||||
case GreaterEqualThan:
|
||||
return other >= number;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
VersionTerm VersionTerm::fromJson(const QJsonValue &v)
|
||||
{
|
||||
VersionTerm result;
|
||||
if (!v.isObject())
|
||||
return result;
|
||||
const QJsonObject o = v.toObject();
|
||||
result.number = QVersionNumber::fromString(o.value(valueKey()).toString());
|
||||
const QString opS = o.value(opKey()).toString();
|
||||
for (size_t i = 0; i < sizeof(operators) / sizeof(operators[0]); ++i) {
|
||||
if (opS == QLatin1String(operators[i])) {
|
||||
result.op = static_cast<Operator>(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// OS term consisting of name and optional version found in
|
||||
// under "os" in main array and in "exceptions" lists.
|
||||
struct OsTypeTerm
|
||||
{
|
||||
static OsTypeTerm fromJson(const QJsonValue &v);
|
||||
static QString hostOs();
|
||||
static QVersionNumber hostKernelVersion() { return QVersionNumber::fromString(QSysInfo::kernelVersion()); }
|
||||
|
||||
bool isNull() const { return type.isEmpty(); }
|
||||
bool matches(const QString &osName, const QVersionNumber &kernelVersion) const
|
||||
{
|
||||
if (isNull() || osName.isEmpty() || kernelVersion.isNull()) {
|
||||
qWarning() << Q_FUNC_INFO << "called with invalid parameters";
|
||||
return false;
|
||||
}
|
||||
if (type != osName)
|
||||
return false;
|
||||
return versionTerm.isNull() || versionTerm.matches(kernelVersion);
|
||||
}
|
||||
|
||||
QString type;
|
||||
VersionTerm versionTerm;
|
||||
};
|
||||
|
||||
OsTypeTerm OsTypeTerm::fromJson(const QJsonValue &v)
|
||||
{
|
||||
OsTypeTerm result;
|
||||
if (!v.isObject())
|
||||
return result;
|
||||
const QJsonObject o = v.toObject();
|
||||
result.type = o.value(typeKey()).toString();
|
||||
result.versionTerm = VersionTerm::fromJson(o.value(versionKey()));
|
||||
return result;
|
||||
}
|
||||
|
||||
QString OsTypeTerm::hostOs()
|
||||
{
|
||||
// Determine Host OS.
|
||||
#if defined(Q_OS_WIN)
|
||||
return QStringLiteral("win");
|
||||
#elif defined(Q_OS_LINUX)
|
||||
return QStringLiteral("linux");
|
||||
#elif defined(Q_OS_OSX)
|
||||
return QStringLiteral("macosx");
|
||||
#elif defined(Q_OS_ANDROID)
|
||||
return QStringLiteral("android");
|
||||
#else
|
||||
return QString();
|
||||
#endif
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
typedef QJsonArray::ConstIterator JsonArrayConstIt;
|
||||
|
||||
static inline bool contains(const QJsonArray &a, unsigned needle)
|
||||
{
|
||||
for (JsonArrayConstIt it = a.constBegin(), cend = a.constEnd(); it != cend; ++it) {
|
||||
if (needle == it->toString().toUInt(Q_NULLPTR, /* base */ 0))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static QString msgSyntaxWarning(const QJsonObject &object, const QString &what)
|
||||
{
|
||||
QString result;
|
||||
QTextStream(&result) << "Id " << object.value(idKey()).toInt()
|
||||
<< " (\"" << object.value(descriptionKey()).toString()
|
||||
<< "\"): " << what;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Check whether an entry matches. Called recursively for
|
||||
// "exceptions" list.
|
||||
|
||||
static bool matches(const QJsonObject &object,
|
||||
const QString &osName,
|
||||
const QVersionNumber &kernelVersion,
|
||||
const QOpenGLConfig::Gpu &gpu)
|
||||
{
|
||||
const OsTypeTerm os = OsTypeTerm::fromJson(object.value(osKey()));
|
||||
if (!os.isNull() && !os.matches(osName, kernelVersion))
|
||||
return false;
|
||||
|
||||
const QJsonValue exceptionsV = object.value(exceptionsKey());
|
||||
if (exceptionsV.isArray()) {
|
||||
const QJsonArray exceptionsA = exceptionsV.toArray();
|
||||
for (JsonArrayConstIt it = exceptionsA.constBegin(), cend = exceptionsA.constEnd(); it != cend; ++it) {
|
||||
if (matches(it->toObject(), osName, kernelVersion, gpu))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const QJsonValue vendorV = object.value(vendorIdKey());
|
||||
if (vendorV.isString()) {
|
||||
if (gpu.vendorId != vendorV.toString().toUInt(Q_NULLPTR, /* base */ 0))
|
||||
return false;
|
||||
} else {
|
||||
if (object.contains(glVendorKey())) {
|
||||
qWarning().nospace() << "Id " << object.value(idKey()).toInt()
|
||||
<< ": Matching by " << glVendorKey() << " is not implemented.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (gpu.deviceId) {
|
||||
const QJsonValue deviceIdV = object.value(deviceIdKey());
|
||||
switch (deviceIdV.type()) {
|
||||
case QJsonValue::Array:
|
||||
if (!contains(deviceIdV.toArray(), gpu.deviceId))
|
||||
return false;
|
||||
break;
|
||||
case QJsonValue::Undefined:
|
||||
case QJsonValue::Null:
|
||||
break;
|
||||
default:
|
||||
qWarning().noquote()
|
||||
<< msgSyntaxWarning(object,
|
||||
QLatin1String("Device ID must be of type array."));
|
||||
}
|
||||
}
|
||||
if (!gpu.driverVersion.isNull()) {
|
||||
const QJsonValue driverVersionV = object.value(driverVersionKey());
|
||||
switch (driverVersionV.type()) {
|
||||
case QJsonValue::Object:
|
||||
if (!VersionTerm::fromJson(driverVersionV).matches(gpu.driverVersion))
|
||||
return false;
|
||||
break;
|
||||
case QJsonValue::Undefined:
|
||||
case QJsonValue::Null:
|
||||
break;
|
||||
default:
|
||||
qWarning().noquote()
|
||||
<< msgSyntaxWarning(object,
|
||||
QLatin1String("Driver version must be of type object."));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool readGpuFeatures(const QOpenGLConfig::Gpu &gpu,
|
||||
const QString &osName,
|
||||
const QVersionNumber &kernelVersion,
|
||||
const QJsonDocument &doc,
|
||||
QSet<QString> *result,
|
||||
QString *errorMessage)
|
||||
{
|
||||
result->clear();
|
||||
errorMessage->clear();
|
||||
const QJsonValue entriesV = doc.object().value(QStringLiteral("entries"));
|
||||
if (!entriesV.isArray()) {
|
||||
*errorMessage = QLatin1String("No entries read.");
|
||||
return false;
|
||||
}
|
||||
|
||||
const QJsonArray entriesA = entriesV.toArray();
|
||||
for (JsonArrayConstIt eit = entriesA.constBegin(), ecend = entriesA.constEnd(); eit != ecend; ++eit) {
|
||||
if (eit->isObject()) {
|
||||
const QJsonObject object = eit->toObject();
|
||||
if (matches(object, osName, kernelVersion, gpu)) {
|
||||
const QJsonValue featuresListV = object.value(featuresKey());
|
||||
if (featuresListV.isArray()) {
|
||||
const QJsonArray featuresListA = featuresListV.toArray();
|
||||
for (JsonArrayConstIt fit = featuresListA.constBegin(), fcend = featuresListA.constEnd(); fit != fcend; ++fit)
|
||||
result->insert(fit->toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool readGpuFeatures(const QOpenGLConfig::Gpu &gpu,
|
||||
const QString &osName,
|
||||
const QVersionNumber &kernelVersion,
|
||||
const QByteArray &jsonAsciiData,
|
||||
QSet<QString> *result, QString *errorMessage)
|
||||
{
|
||||
result->clear();
|
||||
errorMessage->clear();
|
||||
QJsonParseError error;
|
||||
const QJsonDocument document = QJsonDocument::fromJson(jsonAsciiData, &error);
|
||||
if (document.isNull()) {
|
||||
const int lineNumber = 1 + jsonAsciiData.left(error.offset).count('\n');
|
||||
QTextStream str(errorMessage);
|
||||
str << "Failed to parse data: \"" << error.errorString()
|
||||
<< "\" at line " << lineNumber << " (offset: "
|
||||
<< error.offset << ").";
|
||||
return false;
|
||||
}
|
||||
return readGpuFeatures(gpu, osName, kernelVersion, document, result, errorMessage);
|
||||
}
|
||||
|
||||
static bool readGpuFeatures(const QOpenGLConfig::Gpu &gpu,
|
||||
const QString &osName,
|
||||
const QVersionNumber &kernelVersion,
|
||||
const QString &fileName,
|
||||
QSet<QString> *result, QString *errorMessage)
|
||||
{
|
||||
result->clear();
|
||||
errorMessage->clear();
|
||||
QFile file(fileName);
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
QTextStream str(errorMessage);
|
||||
str << "Cannot open \"" << QDir::toNativeSeparators(fileName) << "\": "
|
||||
<< file.errorString();
|
||||
return false;
|
||||
}
|
||||
const bool success = readGpuFeatures(gpu, osName, kernelVersion, file.readAll(), result, errorMessage);
|
||||
if (!success) {
|
||||
errorMessage->prepend(QLatin1String("Error reading \"")
|
||||
+ QDir::toNativeSeparators(fileName)
|
||||
+ QLatin1String("\": "));
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
QSet<QString> QOpenGLConfig::gpuFeatures(const QOpenGLConfig::Gpu &gpu,
|
||||
const QString &osName,
|
||||
const QVersionNumber &kernelVersion,
|
||||
const QJsonDocument &doc)
|
||||
{
|
||||
QSet<QString> result;
|
||||
QString errorMessage;
|
||||
if (!readGpuFeatures(gpu, osName, kernelVersion, doc, &result, &errorMessage))
|
||||
qWarning().noquote() << errorMessage;
|
||||
return result;
|
||||
}
|
||||
|
||||
QSet<QString> QOpenGLConfig::gpuFeatures(const QOpenGLConfig::Gpu &gpu,
|
||||
const QString &osName,
|
||||
const QVersionNumber &kernelVersion,
|
||||
const QString &fileName)
|
||||
{
|
||||
QSet<QString> result;
|
||||
QString errorMessage;
|
||||
if (!readGpuFeatures(gpu, osName, kernelVersion, fileName, &result, &errorMessage))
|
||||
qWarning().noquote() << errorMessage;
|
||||
return result;
|
||||
}
|
||||
|
||||
QSet<QString> QOpenGLConfig::gpuFeatures(const Gpu &gpu, const QJsonDocument &doc)
|
||||
{
|
||||
return gpuFeatures(gpu, OsTypeTerm::hostOs(), OsTypeTerm::hostKernelVersion(), doc);
|
||||
}
|
||||
|
||||
QSet<QString> QOpenGLConfig::gpuFeatures(const Gpu &gpu, const QString &fileName)
|
||||
{
|
||||
return gpuFeatures(gpu, OsTypeTerm::hostOs(), OsTypeTerm::hostKernelVersion(), fileName);
|
||||
}
|
||||
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
|
|
|||
|
|
@ -48,9 +48,13 @@
|
|||
#include <qopengl.h>
|
||||
#include <private/qopenglcontext_p.h>
|
||||
#include <QtCore/qset.h>
|
||||
#include <QtCore/qstring.h>
|
||||
#include <private/qversionnumber_p.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QJsonDocument;
|
||||
|
||||
class Q_GUI_EXPORT QOpenGLExtensionMatcher
|
||||
{
|
||||
public:
|
||||
|
|
@ -67,6 +71,28 @@ private:
|
|||
QSet<QByteArray> m_extensions;
|
||||
};
|
||||
|
||||
class Q_GUI_EXPORT QOpenGLConfig
|
||||
{
|
||||
public:
|
||||
struct Gpu {
|
||||
Gpu() : vendorId(0), deviceId(0) {}
|
||||
bool isValid() const { return deviceId; }
|
||||
|
||||
uint vendorId;
|
||||
uint deviceId;
|
||||
QVersionNumber driverVersion;
|
||||
};
|
||||
|
||||
static QSet<QString> gpuFeatures(const Gpu &gpu,
|
||||
const QString &osName, const QVersionNumber &kernelVersion,
|
||||
const QJsonDocument &doc);
|
||||
static QSet<QString> gpuFeatures(const Gpu &gpu,
|
||||
const QString &osName, const QVersionNumber &kernelVersion,
|
||||
const QString &fileName);
|
||||
static QSet<QString> gpuFeatures(const Gpu &gpu, const QJsonDocument &doc);
|
||||
static QSet<QString> gpuFeatures(const Gpu &gpu, const QString &fileName);
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QOPENGL_H
|
||||
|
|
|
|||
|
|
@ -38,6 +38,12 @@
|
|||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QTextStream>
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QFile>
|
||||
#include <QtCore/QFileInfo>
|
||||
#include <QtCore/QStandardPaths>
|
||||
#include <QtCore/QLibraryInfo>
|
||||
|
||||
#include <private/qopengl_p.h>
|
||||
|
||||
#ifndef Q_OS_WINCE
|
||||
# include <QtCore/qt_windows.h>
|
||||
|
|
@ -183,6 +189,57 @@ QWindowsOpenGLTester::Renderer QWindowsOpenGLTester::requestedRenderer()
|
|||
return QWindowsOpenGLTester::InvalidRenderer;
|
||||
}
|
||||
|
||||
#ifndef Q_OS_WINCE
|
||||
|
||||
static inline QString resolveBugListFile(const QString &fileName)
|
||||
{
|
||||
if (QFileInfo(fileName).isAbsolute())
|
||||
return fileName;
|
||||
// Try QLibraryInfo::SettingsPath which is typically empty unless specified in qt.conf,
|
||||
// then resolve via QStandardPaths::ConfigLocation.
|
||||
const QString settingsPath = QLibraryInfo::location(QLibraryInfo::SettingsPath);
|
||||
if (!settingsPath.isEmpty()) { // SettingsPath is empty unless specified in qt.conf.
|
||||
const QFileInfo fi(settingsPath + QLatin1Char('/') + fileName);
|
||||
if (fi.isFile())
|
||||
return fi.absoluteFilePath();
|
||||
}
|
||||
return QStandardPaths::locate(QStandardPaths::ConfigLocation, fileName);
|
||||
}
|
||||
|
||||
static void readDriverBugList(const GpuDescription &gpu,
|
||||
QWindowsOpenGLTester::Renderers *result)
|
||||
{
|
||||
const char bugListFileVar[] = "QT_OPENGL_BUGLIST";
|
||||
if (!qEnvironmentVariableIsSet(bugListFileVar))
|
||||
return;
|
||||
const QString fileName = resolveBugListFile(QFile::decodeName(qgetenv(bugListFileVar)));
|
||||
if (fileName.isEmpty())
|
||||
return;
|
||||
QOpenGLConfig::Gpu qgpu;
|
||||
qgpu.deviceId = gpu.deviceId;
|
||||
qgpu.vendorId = gpu.vendorId;
|
||||
qgpu.driverVersion = gpu.driverVersion;
|
||||
const QSet<QString> features = QOpenGLConfig::gpuFeatures(qgpu, fileName);
|
||||
if (features.contains(QStringLiteral("disable_desktopgl"))) { // Qt-specific
|
||||
qCWarning(lcQpaGl) << "Disabling Desktop GL: " << gpu;
|
||||
*result &= ~QWindowsOpenGLTester::DesktopGl;
|
||||
}
|
||||
if (features.contains(QStringLiteral("disable_angle"))) { // Qt-specific keyword
|
||||
qCWarning(lcQpaGl) << "Disabling ANGLE: " << gpu;
|
||||
*result &= ~QWindowsOpenGLTester::GlesMask;
|
||||
} else {
|
||||
if (features.contains(QStringLiteral("disable_d3d11"))) { // standard keyword
|
||||
qCWarning(lcQpaGl) << "Disabling D3D11: " << gpu;
|
||||
*result &= ~QWindowsOpenGLTester::AngleRendererD3d11;
|
||||
}
|
||||
if (features.contains(QStringLiteral("disable_d3d9"))) { // Qt-specific
|
||||
qCWarning(lcQpaGl) << "Disabling D3D9: " << gpu;
|
||||
*result &= ~QWindowsOpenGLTester::AngleRendererD3d9;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // !Q_OS_WINCE
|
||||
|
||||
static inline QWindowsOpenGLTester::Renderers
|
||||
detectSupportedRenderers(const GpuDescription &gpu, bool glesOnly)
|
||||
{
|
||||
|
|
@ -196,6 +253,9 @@ static inline QWindowsOpenGLTester::Renderers
|
|||
|
||||
if (!glesOnly && QWindowsOpenGLTester::testDesktopGL())
|
||||
result |= QWindowsOpenGLTester::DesktopGl;
|
||||
|
||||
readDriverBugList(gpu, &result);
|
||||
|
||||
return result;
|
||||
#else // !Q_OS_WINCE
|
||||
return QWindowsOpenGLTester::Gles;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,106 @@
|
|||
{
|
||||
"name": "gpu driver bug list example",
|
||||
"version": "0.9",
|
||||
"entries": [
|
||||
{
|
||||
"id": 1,
|
||||
"description": "non-applicable feature: different OS",
|
||||
"vendor_id": "0x10de",
|
||||
"device_id": [ "0x0DE9" ],
|
||||
"os": {
|
||||
"type": "android"
|
||||
},
|
||||
"features": [
|
||||
"wrong_feature1"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"description": "non-applicable feature: ancient OS",
|
||||
"vendor_id": "0x10de",
|
||||
"device_id": [ "0x0DE9" ],
|
||||
"os": {
|
||||
"type": "win",
|
||||
"version": {
|
||||
"op": "<",
|
||||
"value": "4.0"
|
||||
}
|
||||
},
|
||||
"features": [
|
||||
"wrong_feature2"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"description": "non-applicable feature: excluded OS",
|
||||
"vendor_id": "0x10de",
|
||||
"device_id": [ "0x0DE9" ],
|
||||
"exceptions": [
|
||||
{
|
||||
"os": {
|
||||
"type": "win"
|
||||
}
|
||||
}
|
||||
],
|
||||
"features": [
|
||||
"wrong_feature3"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"description": "non-applicable feature: wrong vendor",
|
||||
"vendor_id": "0x10df",
|
||||
"device_id": [ "0x0DE9" ],
|
||||
"os": {
|
||||
"type": "win"
|
||||
},
|
||||
"features": [
|
||||
"wrong_feature4"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"description": "non-applicable feature: wrong device",
|
||||
"vendor_id": "0x10de",
|
||||
"device_id": [ "0x0DEA" ],
|
||||
"os": {
|
||||
"type": "win"
|
||||
},
|
||||
"features": [
|
||||
"wrong_feature5"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"description": "feature 1",
|
||||
"vendor_id": "0x10de",
|
||||
"device_id": [ "0x0DE9" ],
|
||||
"os": {
|
||||
"type": "win"
|
||||
},
|
||||
"driver_version": {
|
||||
"op": ">",
|
||||
"value": "9.18.0.0"
|
||||
},
|
||||
"features": [
|
||||
"feature1"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"description": "non-applicable feature: too new driver",
|
||||
"vendor_id": "0x10de",
|
||||
"device_id": [ "0x0DE9" ],
|
||||
"os": {
|
||||
"type": "win"
|
||||
},
|
||||
"driver_version": {
|
||||
"op": "<=",
|
||||
"value": "9.17.13.4344"
|
||||
},
|
||||
"features": [
|
||||
"feature1"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -8,3 +8,4 @@ TARGET = tst_qopenglconfig
|
|||
QT += gui-private core-private testlib
|
||||
|
||||
SOURCES += tst_qopenglconfig.cpp
|
||||
OTHER_FILES = buglist.json
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@
|
|||
#include <private/qguiapplication_p.h>
|
||||
#include <qpa/qplatformintegration.h>
|
||||
#include <qpa/qplatformnativeinterface.h>
|
||||
#include <private/qopengl_p.h>
|
||||
|
||||
#include <QtTest/QtTest>
|
||||
|
||||
|
|
@ -105,6 +106,7 @@ class tst_QOpenGlConfig : public QObject
|
|||
private slots:
|
||||
void testConfiguration();
|
||||
void testGlConfiguration();
|
||||
void testBugList();
|
||||
};
|
||||
|
||||
static void dumpConfiguration(QTextStream &str)
|
||||
|
|
@ -217,6 +219,41 @@ void tst_QOpenGlConfig::testGlConfiguration()
|
|||
qDebug().noquote() << '\n' << result;
|
||||
}
|
||||
|
||||
static inline QByteArray msgSetMismatch(const QSet<QString> &expected,
|
||||
const QSet<QString> &actual)
|
||||
{
|
||||
const QString result = QStringList(expected.toList()).join(QLatin1Char(','))
|
||||
+ QLatin1String(" != ")
|
||||
+ QStringList(actual.toList()).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";
|
||||
|
||||
QOpenGLConfig::Gpu gpu;
|
||||
gpu.vendorId = 0x10DE;
|
||||
gpu.deviceId = 0x0DE9;
|
||||
|
||||
#ifdef Q_COMPILER_INITIALIZER_LISTS
|
||||
gpu.driverVersion = QVersionNumber({9, 18, 13, 4460});
|
||||
#else
|
||||
gpu.driverVersion = QVersionNumber(QVector<int>() << 9 << 18 << 13 << 4460);
|
||||
#endif
|
||||
const QSet<QString> actualFeatures =
|
||||
QOpenGLConfig::gpuFeatures(gpu, QStringLiteral("win"),
|
||||
QVersionNumber(6, 3), fileName);
|
||||
QVERIFY2(expectedFeatures == actualFeatures,
|
||||
msgSetMismatch(expectedFeatures, actualFeatures));
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QOpenGlConfig)
|
||||
|
||||
#include "tst_qopenglconfig.moc"
|
||||
|
|
|
|||
Loading…
Reference in New Issue