12#include <QtCore/QDebug>
13#include <QtCore/QJsonDocument>
14#include <QtCore/QJsonValue>
15#include <QtCore/QJsonObject>
16#include <QtCore/QJsonArray>
17#include <QtCore/QTextStream>
18#include <QtCore/QFile>
23using namespace Qt::StringLiterals;
25#if defined(QT_OPENGL_3)
26typedef const GLubyte * (QOPENGLF_APIENTRYP qt_glGetStringi)(GLenum, GLuint);
29#ifndef GL_CONTEXT_LOST
30#define GL_CONTEXT_LOST 0x0507
33QOpenGLExtensionMatcher::QOpenGLExtensionMatcher()
35 QOpenGLContext *ctx = QOpenGLContext::currentContext();
37 qWarning(
"QOpenGLExtensionMatcher::QOpenGLExtensionMatcher: No context");
40 QOpenGLFunctions *funcs = ctx->functions();
41 const char *extensionStr =
nullptr;
43 if (ctx->isOpenGLES() || ctx->format().majorVersion() < 3)
44 extensionStr =
reinterpret_cast<
const char *>(funcs->glGetString(GL_EXTENSIONS));
47 QByteArray ba(extensionStr);
48 QList<QByteArray> extensions = ba.split(
' ');
49 m_extensions = QSet<QByteArray>(extensions.constBegin(), extensions.constEnd());
54 GLenum error = funcs->glGetError();
55 if (error == GL_NO_ERROR)
57 if (error == GL_CONTEXT_LOST)
60 qt_glGetStringi glGetStringi = (qt_glGetStringi)ctx->getProcAddress(
"glGetStringi");
65 GLint numExtensions = 0;
66 funcs->glGetIntegerv(GL_NUM_EXTENSIONS, &numExtensions);
68 for (
int i = 0; i < numExtensions; ++i) {
69 const char *str =
reinterpret_cast<
const char *>(glGetStringi(GL_EXTENSIONS, i));
70 m_extensions.insert(str);
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
99QDebug operator<<(QDebug d,
const QOpenGLConfig::Gpu &g)
101 QDebugStateSaver s(d);
105 d <<
"vendor=" << Qt::hex << Qt::showbase <<g.vendorId <<
", device=" << g.deviceId
106 <<
"version=" << g.driverVersion;
116static inline bool contains(
const QJsonArray &haystack,
unsigned needle)
118 for (
JsonArrayConstIt it = haystack.constBegin(), cend = haystack.constEnd(); it != cend; ++it) {
119 if (needle == it->toString().toUInt(
nullptr, 0))
125static inline bool contains(
const QJsonArray &haystack,
const QString &needle)
127 for (
JsonArrayConstIt it = haystack.constBegin(), cend = haystack.constEnd(); it != cend; ++it) {
128 if (needle == it->toString())
135enum Operator { NotEqual, LessThan, LessEqualThan, Equals, GreaterThan, GreaterEqualThan };
136static const char operators[][3] = {
"!=",
"<",
"<=",
"=",
">",
">="};
141 VersionTerm() : op(NotEqual) {}
142 static VersionTerm fromJson(
const QJsonValue &v);
143 bool isNull()
const {
return number.isNull(); }
144 bool matches(
const QVersionNumber &other)
const;
146 QVersionNumber number;
150bool VersionTerm::matches(
const QVersionNumber &other)
const
152 if (isNull() || other.isNull()) {
153 qWarning(
"called with invalid parameters");
158 return other != number;
160 return other < number;
162 return other <= number;
164 return other == number;
166 return other > number;
167 case GreaterEqualThan:
168 return other >= number;
173VersionTerm VersionTerm::fromJson(
const QJsonValue &v)
178 const QJsonObject o = v.toObject();
179 result.number = QVersionNumber::fromString(o.value(
"value"_L1).toStringView());
180 const auto opS = o.value(
"op"_L1).toStringView();
181 for (size_t i = 0; i <
sizeof(operators) /
sizeof(operators[0]); ++i) {
182 if (opS == QLatin1StringView(operators[i])) {
183 result.op =
static_cast<Operator>(i);
194 static OsTypeTerm fromJson(
const QJsonValue &v);
195 static QString hostOs();
196 static QVersionNumber hostKernelVersion() {
return QVersionNumber::fromString(QSysInfo::kernelVersion()); }
197 static QString hostOsRelease() {
199 if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows11)
207 bool isNull()
const {
return type.isEmpty(); }
208 bool matches(
const QString &osName,
const QVersionNumber &kernelVersion,
const QString &osRelease)
const
210 if (isNull() || osName.isEmpty() || kernelVersion.isNull()) {
211 qWarning(
"called with invalid parameters");
216 if (!versionTerm.isNull() && !versionTerm.matches(kernelVersion))
219 if (!release.isEmpty() && !contains(release, osRelease))
225 VersionTerm versionTerm;
229OsTypeTerm OsTypeTerm::fromJson(
const QJsonValue &v)
234 const QJsonObject o = v.toObject();
235 result.type = o.value(
"type"_L1).toString();
236 result.versionTerm = VersionTerm::fromJson(o.value(
"version"_L1));
237 result.release = o.value(
"release"_L1).toArray();
241QString OsTypeTerm::hostOs()
245 return QStringLiteral(
"win");
246#elif defined(Q_OS_LINUX)
247 return QStringLiteral(
"linux");
248#elif defined(Q_OS_MACOS)
249 return QStringLiteral(
"macosx");
250#elif defined(Q_OS_ANDROID)
251 return QStringLiteral(
"android");
261 QTextStream(&result) <<
"Id " << object.value(
"id"_L1).toInt()
262 <<
" (\"" << object.value(
"description"_L1).toString()
271 const QString &osName,
272 const QVersionNumber &kernelVersion,
273 const QString &osRelease,
274 const QOpenGLConfig::Gpu &gpu)
276 const OsTypeTerm os = OsTypeTerm::fromJson(object.value(
"os"_L1));
277 if (!os.isNull() && !os.matches(osName, kernelVersion, osRelease))
280 const QJsonValue exceptionsV = object.value(
"exceptions"_L1);
281 if (exceptionsV.isArray()) {
282 const QJsonArray exceptionsA = exceptionsV.toArray();
283 for (
JsonArrayConstIt it = exceptionsA.constBegin(), cend = exceptionsA.constEnd(); it != cend; ++it) {
284 if (matches(it->toObject(), osName, kernelVersion, osRelease, gpu))
289 const QJsonValue vendorV = object.value(
"vendor_id"_L1);
290 if (vendorV.isString()) {
291 if (gpu.vendorId != vendorV.toString().toUInt(
nullptr, 0))
294 if (object.contains(
"gl_vendor"_L1)) {
295 const QByteArray glVendorV = object.value(
"gl_vendor"_L1).toString().toUtf8();
296 if (!gpu.glVendor.contains(glVendorV))
302 const QJsonValue deviceIdV = object.value(
"device_id"_L1);
303 switch (deviceIdV.type()) {
304 case QJsonValue::Array:
305 if (!contains(deviceIdV.toArray(), gpu.deviceId))
308 case QJsonValue::Undefined:
309 case QJsonValue::Null:
313 << msgSyntaxWarning(object,
"Device ID must be of type array."_L1);
316 if (!gpu.driverVersion.isNull()) {
317 const QJsonValue driverVersionV = object.value(
"driver_version"_L1);
318 switch (driverVersionV.type()) {
319 case QJsonValue::Object:
320 if (!VersionTerm::fromJson(driverVersionV).matches(gpu.driverVersion))
323 case QJsonValue::Undefined:
324 case QJsonValue::Null:
328 << msgSyntaxWarning(object,
"Driver version must be of type object."_L1);
332 if (!gpu.driverDescription.isEmpty()) {
333 const QJsonValue driverDescriptionV = object.value(
"driver_description"_L1);
334 if (driverDescriptionV.isString()) {
335 if (!gpu.driverDescription.contains(driverDescriptionV.toString().toUtf8()))
344 const QString &osName,
345 const QVersionNumber &kernelVersion,
346 const QString &osRelease,
347 const QJsonDocument &doc,
348 QSet<QString> *result,
349 QString *errorMessage)
352 errorMessage->clear();
353 const QJsonValue entriesV = doc.object().value(
"entries"_L1);
354 if (!entriesV.isArray()) {
355 *errorMessage =
"No entries read."_L1;
359 const QJsonArray entriesA = entriesV.toArray();
360 for (
JsonArrayConstIt eit = entriesA.constBegin(), ecend = entriesA.constEnd(); eit != ecend; ++eit) {
361 if (eit->isObject()) {
362 const QJsonObject object = eit->toObject();
363 if (matches(object, osName, kernelVersion, osRelease, gpu)) {
364 const QJsonValue featuresListV = object.value(
"features"_L1);
365 if (featuresListV.isArray()) {
366 const QJsonArray featuresListA = featuresListV.toArray();
367 for (
JsonArrayConstIt fit = featuresListA.constBegin(), fcend = featuresListA.constEnd(); fit != fcend; ++fit)
368 result->insert(fit->toString());
377 const QString &osName,
378 const QVersionNumber &kernelVersion,
379 const QString &osRelease,
380 const QByteArray &jsonAsciiData,
381 QSet<QString> *result, QString *errorMessage)
384 errorMessage->clear();
385 QJsonParseError error;
386 const QJsonDocument document = QJsonDocument::fromJson(jsonAsciiData, &error);
387 if (document.isNull()) {
388 const qsizetype lineNumber =
389 QByteArrayView(jsonAsciiData).left(error.offset).count(
'\n') + 1;
390 QTextStream str(errorMessage);
391 str <<
"Failed to parse data: \"" << error.errorString()
392 <<
"\" at line " << lineNumber <<
" (offset: "
393 << error.offset <<
").";
396 return readGpuFeatures(gpu, osName, kernelVersion, osRelease, document, result, errorMessage);
400 const QString &osName,
401 const QVersionNumber &kernelVersion,
402 const QString &osRelease,
403 const QString &fileName,
404 QSet<QString> *result, QString *errorMessage)
407 errorMessage->clear();
408 QFile file(fileName);
409 if (!file.open(QIODevice::ReadOnly)) {
410 QTextStream str(errorMessage);
411 str <<
"Cannot open \"" << QDir::toNativeSeparators(fileName) <<
"\": "
412 << file.errorString();
415 const bool success = readGpuFeatures(gpu, osName, kernelVersion, osRelease, file.readAll(), result, errorMessage);
417 errorMessage->prepend(
"Error reading \""_L1
418 + QDir::toNativeSeparators(fileName)
424QSet<QString> QOpenGLConfig::gpuFeatures(
const QOpenGLConfig::Gpu &gpu,
425 const QString &osName,
426 const QVersionNumber &kernelVersion,
427 const QString &osRelease,
428 const QJsonDocument &doc)
430 QSet<QString> result;
431 QString errorMessage;
432 if (!readGpuFeatures(gpu, osName, kernelVersion, osRelease, doc, &result, &errorMessage))
433 qWarning().noquote() << errorMessage;
437QSet<QString> QOpenGLConfig::gpuFeatures(
const QOpenGLConfig::Gpu &gpu,
438 const QString &osName,
439 const QVersionNumber &kernelVersion,
440 const QString &osRelease,
441 const QString &fileName)
443 QSet<QString> result;
444 QString errorMessage;
445 if (!readGpuFeatures(gpu, osName, kernelVersion, osRelease, fileName, &result, &errorMessage))
446 qWarning().noquote() << errorMessage;
450QSet<QString> QOpenGLConfig::gpuFeatures(
const Gpu &gpu,
const QJsonDocument &doc)
452 return gpuFeatures(gpu, OsTypeTerm::hostOs(), OsTypeTerm::hostKernelVersion(), OsTypeTerm::hostOsRelease(), doc);
455QSet<QString> QOpenGLConfig::gpuFeatures(
const Gpu &gpu,
const QString &fileName)
457 return gpuFeatures(gpu, OsTypeTerm::hostOs(), OsTypeTerm::hostKernelVersion(), OsTypeTerm::hostOsRelease(), fileName);
460QOpenGLConfig::Gpu QOpenGLConfig::Gpu::fromContext()
462 QOpenGLContext *ctx = QOpenGLContext::currentContext();
463 QScopedPointer<QOpenGLContext> tmpContext;
464 QScopedPointer<QOffscreenSurface> tmpSurface;
466 tmpContext.reset(
new QOpenGLContext);
467 if (!tmpContext->create()) {
468 qWarning(
"QOpenGLConfig::Gpu::fromContext: Failed to create temporary context");
469 return QOpenGLConfig::Gpu();
471 tmpSurface.reset(
new QOffscreenSurface);
472 tmpSurface->setFormat(tmpContext->format());
473 tmpSurface->create();
474 tmpContext->makeCurrent(tmpSurface.data());
477 QOpenGLConfig::Gpu gpu;
478 ctx = QOpenGLContext::currentContext();
479 const GLubyte *p = ctx->functions()->glGetString(GL_VENDOR);
481 gpu.glVendor = QByteArray(
reinterpret_cast<
const char *>(p));
Combined button and popup list for selecting options.
static bool contains(const QJsonArray &haystack, const QString &needle)
static bool readGpuFeatures(const QOpenGLConfig::Gpu &gpu, const QString &osName, const QVersionNumber &kernelVersion, const QString &osRelease, const QJsonDocument &doc, QSet< QString > *result, QString *errorMessage)
static QString msgSyntaxWarning(const QJsonObject &object, const QString &what)
static bool readGpuFeatures(const QOpenGLConfig::Gpu &gpu, const QString &osName, const QVersionNumber &kernelVersion, const QString &osRelease, const QByteArray &jsonAsciiData, QSet< QString > *result, QString *errorMessage)
static bool contains(const QJsonArray &haystack, unsigned needle)
static bool readGpuFeatures(const QOpenGLConfig::Gpu &gpu, const QString &osName, const QVersionNumber &kernelVersion, const QString &osRelease, const QString &fileName, QSet< QString > *result, QString *errorMessage)
static bool matches(const QJsonObject &object, const QString &osName, const QVersionNumber &kernelVersion, const QString &osRelease, const QOpenGLConfig::Gpu &gpu)
QJsonArray::ConstIterator JsonArrayConstIt