QtBase  v6.3.1
qopengl.cpp
Go to the documentation of this file.
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 QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
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 Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qopengl.h"
41 #include "qopengl_p.h"
42 
43 #include "qopenglcontext.h"
44 #include "qopenglfunctions.h"
46 #include "qoffscreensurface.h"
47 
48 #include <QtCore/QDebug>
49 #include <QtCore/QJsonDocument>
50 #include <QtCore/QJsonValue>
51 #include <QtCore/QJsonObject>
52 #include <QtCore/QJsonArray>
53 #include <QtCore/QTextStream>
54 #include <QtCore/QFile>
55 #include <QtCore/QDir>
56 
58 
59 #if defined(QT_OPENGL_3)
60 typedef const GLubyte * (QOPENGLF_APIENTRYP qt_glGetStringi)(GLenum, GLuint);
61 #endif
62 
63 #ifndef GL_CONTEXT_LOST
64 #define GL_CONTEXT_LOST 0x0507
65 #endif
66 
68 {
70  if (!ctx) {
71  qWarning("QOpenGLExtensionMatcher::QOpenGLExtensionMatcher: No context");
72  return;
73  }
74  QOpenGLFunctions *funcs = ctx->functions();
75  const char *extensionStr = nullptr;
76 
77  if (ctx->isOpenGLES() || ctx->format().majorVersion() < 3)
78  extensionStr = reinterpret_cast<const char *>(funcs->glGetString(GL_EXTENSIONS));
79 
80  if (extensionStr) {
81  QByteArray ba(extensionStr);
84  } else {
85 #ifdef QT_OPENGL_3
86  // clear error state
87  while (true) { // Clear error state.
88  GLenum error = funcs->glGetError();
89  if (error == GL_NO_ERROR)
90  break;
91  if (error == GL_CONTEXT_LOST)
92  return;
93  };
94  qt_glGetStringi glGetStringi = (qt_glGetStringi)ctx->getProcAddress("glGetStringi");
95 
96  if (!glGetStringi)
97  return;
98 
99  GLint numExtensions = 0;
100  funcs->glGetIntegerv(GL_NUM_EXTENSIONS, &numExtensions);
101 
102  for (int i = 0; i < numExtensions; ++i) {
103  const char *str = reinterpret_cast<const char *>(glGetStringi(GL_EXTENSIONS, i));
104  m_extensions.insert(str);
105  }
106 #endif // QT_OPENGL_3
107  }
108 }
109 
110 /* Helpers to read out the list of features matching a device from
111  * a Chromium driver bug list. Note that not all keys are supported and
112  * some may behave differently: gl_vendor is a substring match instead of regex.
113  {
114  "entries": [
115  {
116  "id": 20,
117  "description": "Disable EXT_draw_buffers on GeForce GT 650M on Linux due to driver bugs",
118  "os": {
119  "type": "linux"
120  },
121  // Optional: "exceptions" list
122  "vendor_id": "0x10de",
123  "device_id": ["0x0fd5"],
124  "multi_gpu_category": "any",
125  "features": [
126  "disable_ext_draw_buffers"
127  ]
128  },
129  ....
130  }
131 */
132 
134 {
136  d.nospace();
137  d << "Gpu(";
138  if (g.isValid()) {
139  d << "vendor=" << Qt::hex << Qt::showbase <<g.vendorId << ", device=" << g.deviceId
140  << "version=" << g.driverVersion;
141  } else {
142  d << 0;
143  }
144  d << ')';
145  return d;
146 }
147 
149 
150 static inline bool contains(const QJsonArray &haystack, unsigned needle)
151 {
152  for (JsonArrayConstIt it = haystack.constBegin(), cend = haystack.constEnd(); it != cend; ++it) {
153  if (needle == it->toString().toUInt(nullptr, /* base */ 0))
154  return true;
155  }
156  return false;
157 }
158 
159 static inline bool contains(const QJsonArray &haystack, const QString &needle)
160 {
161  for (JsonArrayConstIt it = haystack.constBegin(), cend = haystack.constEnd(); it != cend; ++it) {
162  if (needle == it->toString())
163  return true;
164  }
165  return false;
166 }
167 
168 namespace {
169 enum Operator { NotEqual, LessThan, LessEqualThan, Equals, GreaterThan, GreaterEqualThan };
170 static const char operators[][3] = {"!=", "<", "<=", "=", ">", ">="};
171 
172 // VersionTerm describing a version term consisting of number and operator
173 // found in os.version and driver_version.
174 struct VersionTerm {
175  VersionTerm() : op(NotEqual) {}
176  static VersionTerm fromJson(const QJsonValue &v);
177  bool isNull() const { return number.isNull(); }
178  bool matches(const QVersionNumber &other) const;
179 
181  Operator op;
182 };
183 
184 bool VersionTerm::matches(const QVersionNumber &other) const
185 {
186  if (isNull() || other.isNull()) {
187  qWarning("called with invalid parameters");
188  return false;
189  }
190  switch (op) {
191  case NotEqual:
192  return other != number;
193  case LessThan:
194  return other < number;
195  case LessEqualThan:
196  return other <= number;
197  case Equals:
198  return other == number;
199  case GreaterThan:
200  return other > number;
201  case GreaterEqualThan:
202  return other >= number;
203  }
204  return false;
205 }
206 
207 VersionTerm VersionTerm::fromJson(const QJsonValue &v)
208 {
209  VersionTerm result;
210  if (!v.isObject())
211  return result;
212  const QJsonObject o = v.toObject();
213  result.number = QVersionNumber::fromString(o.value(QLatin1String("value")).toString());
214  const QString opS = o.value(QLatin1String("op")).toString();
215  for (size_t i = 0; i < sizeof(operators) / sizeof(operators[0]); ++i) {
216  if (opS == QLatin1String(operators[i])) {
217  result.op = static_cast<Operator>(i);
218  break;
219  }
220  }
221  return result;
222 }
223 
224 // OS term consisting of name and optional version found in
225 // under "os" in main array and in "exceptions" lists.
226 struct OsTypeTerm
227 {
228  static OsTypeTerm fromJson(const QJsonValue &v);
229  static QString hostOs();
230  static QVersionNumber hostKernelVersion() { return QVersionNumber::fromString(QSysInfo::kernelVersion()); }
231  static QString hostOsRelease() {
232  QString ver;
233 #ifdef Q_OS_WIN
234  const auto osver = QOperatingSystemVersion::current();
235 #define Q_WINVER(major, minor) (major << 8 | minor)
236  switch (Q_WINVER(osver.majorVersion(), osver.minorVersion())) {
237  case Q_WINVER(6, 1):
238  ver = QStringLiteral("7");
239  break;
240  case Q_WINVER(6, 2):
241  ver = QStringLiteral("8");
242  break;
243  case Q_WINVER(6, 3):
244  ver = QStringLiteral("8.1");
245  break;
246  case Q_WINVER(10, 0):
247  ver = QStringLiteral("10");
248  break;
249  default:
250  break;
251  }
252 #undef Q_WINVER
253 #endif
254  return ver;
255  }
256 
257  bool isNull() const { return type.isEmpty(); }
258  bool matches(const QString &osName, const QVersionNumber &kernelVersion, const QString &osRelease) const
259  {
260  if (isNull() || osName.isEmpty() || kernelVersion.isNull()) {
261  qWarning("called with invalid parameters");
262  return false;
263  }
264  if (type != osName)
265  return false;
266  if (!versionTerm.isNull() && !versionTerm.matches(kernelVersion))
267  return false;
268  // release is a list of Windows versions where the rule should match
269  if (!release.isEmpty() && !contains(release, osRelease))
270  return false;
271  return true;
272  }
273 
274  QString type;
275  VersionTerm versionTerm;
277 };
278 
279 OsTypeTerm OsTypeTerm::fromJson(const QJsonValue &v)
280 {
281  OsTypeTerm result;
282  if (!v.isObject())
283  return result;
284  const QJsonObject o = v.toObject();
285  result.type = o.value(QLatin1String("type")).toString();
286  result.versionTerm = VersionTerm::fromJson(o.value(QLatin1String("version")));
287  result.release = o.value(QLatin1String("release")).toArray();
288  return result;
289 }
290 
291 QString OsTypeTerm::hostOs()
292 {
293  // Determine Host OS.
294 #if defined(Q_OS_WIN)
295  return QStringLiteral("win");
296 #elif defined(Q_OS_LINUX)
297  return QStringLiteral("linux");
298 #elif defined(Q_OS_MACOS)
299  return QStringLiteral("macosx");
300 #elif defined(Q_OS_ANDROID)
301  return QStringLiteral("android");
302 #else
303  return QString();
304 #endif
305 }
306 } // anonymous namespace
307 
308 static QString msgSyntaxWarning(const QJsonObject &object, const QString &what)
309 {
310  QString result;
311  QTextStream(&result) << "Id " << object.value(QLatin1String("id")).toInt()
312  << " (\"" << object.value(QLatin1String("description")).toString()
313  << "\"): " << what;
314  return result;
315 }
316 
317 // Check whether an entry matches. Called recursively for
318 // "exceptions" list.
319 
320 static bool matches(const QJsonObject &object,
321  const QString &osName,
322  const QVersionNumber &kernelVersion,
323  const QString &osRelease,
324  const QOpenGLConfig::Gpu &gpu)
325 {
326  const OsTypeTerm os = OsTypeTerm::fromJson(object.value(QLatin1String("os")));
327  if (!os.isNull() && !os.matches(osName, kernelVersion, osRelease))
328  return false;
329 
330  const QJsonValue exceptionsV = object.value(QLatin1String("exceptions"));
331  if (exceptionsV.isArray()) {
332  const QJsonArray exceptionsA = exceptionsV.toArray();
333  for (JsonArrayConstIt it = exceptionsA.constBegin(), cend = exceptionsA.constEnd(); it != cend; ++it) {
334  if (matches(it->toObject(), osName, kernelVersion, osRelease, gpu))
335  return false;
336  }
337  }
338 
339  const QJsonValue vendorV = object.value(QLatin1String("vendor_id"));
340  if (vendorV.isString()) {
341  if (gpu.vendorId != vendorV.toString().toUInt(nullptr, /* base */ 0))
342  return false;
343  } else {
344  if (object.contains(QLatin1String("gl_vendor"))) {
345  const QByteArray glVendorV = object.value(QLatin1String("gl_vendor")).toString().toUtf8();
346  if (!gpu.glVendor.contains(glVendorV))
347  return false;
348  }
349  }
350 
351  if (gpu.deviceId) {
352  const QJsonValue deviceIdV = object.value(QLatin1String("device_id"));
353  switch (deviceIdV.type()) {
354  case QJsonValue::Array:
355  if (!contains(deviceIdV.toArray(), gpu.deviceId))
356  return false;
357  break;
359  case QJsonValue::Null:
360  break;
361  default:
362  qWarning().noquote()
363  << msgSyntaxWarning(object,
364  QLatin1String("Device ID must be of type array."));
365  }
366  }
367  if (!gpu.driverVersion.isNull()) {
368  const QJsonValue driverVersionV = object.value(QLatin1String("driver_version"));
369  switch (driverVersionV.type()) {
370  case QJsonValue::Object:
371  if (!VersionTerm::fromJson(driverVersionV).matches(gpu.driverVersion))
372  return false;
373  break;
375  case QJsonValue::Null:
376  break;
377  default:
378  qWarning().noquote()
379  << msgSyntaxWarning(object,
380  QLatin1String("Driver version must be of type object."));
381  }
382  }
383 
384  if (!gpu.driverDescription.isEmpty()) {
385  const QJsonValue driverDescriptionV = object.value(QLatin1String("driver_description"));
386  if (driverDescriptionV.isString()) {
387  if (!gpu.driverDescription.contains(driverDescriptionV.toString().toUtf8()))
388  return false;
389  }
390  }
391 
392  return true;
393 }
394 
395 static bool readGpuFeatures(const QOpenGLConfig::Gpu &gpu,
396  const QString &osName,
397  const QVersionNumber &kernelVersion,
398  const QString &osRelease,
399  const QJsonDocument &doc,
401  QString *errorMessage)
402 {
403  result->clear();
404  errorMessage->clear();
405  const QJsonValue entriesV = doc.object().value(QLatin1String("entries"));
406  if (!entriesV.isArray()) {
407  *errorMessage = QLatin1String("No entries read.");
408  return false;
409  }
410 
411  const QJsonArray entriesA = entriesV.toArray();
412  for (JsonArrayConstIt eit = entriesA.constBegin(), ecend = entriesA.constEnd(); eit != ecend; ++eit) {
413  if (eit->isObject()) {
414  const QJsonObject object = eit->toObject();
415  if (matches(object, osName, kernelVersion, osRelease, gpu)) {
416  const QJsonValue featuresListV = object.value(QLatin1String("features"));
417  if (featuresListV.isArray()) {
418  const QJsonArray featuresListA = featuresListV.toArray();
419  for (JsonArrayConstIt fit = featuresListA.constBegin(), fcend = featuresListA.constEnd(); fit != fcend; ++fit)
420  result->insert(fit->toString());
421  }
422  }
423  }
424  }
425  return true;
426 }
427 
428 static bool readGpuFeatures(const QOpenGLConfig::Gpu &gpu,
429  const QString &osName,
430  const QVersionNumber &kernelVersion,
431  const QString &osRelease,
432  const QByteArray &jsonAsciiData,
433  QSet<QString> *result, QString *errorMessage)
434 {
435  result->clear();
436  errorMessage->clear();
438  const QJsonDocument document = QJsonDocument::fromJson(jsonAsciiData, &error);
439  if (document.isNull()) {
440  const int lineNumber = 1 + jsonAsciiData.left(error.offset).count('\n');
441  QTextStream str(errorMessage);
442  str << "Failed to parse data: \"" << error.errorString()
443  << "\" at line " << lineNumber << " (offset: "
444  << error.offset << ").";
445  return false;
446  }
447  return readGpuFeatures(gpu, osName, kernelVersion, osRelease, document, result, errorMessage);
448 }
449 
450 static bool readGpuFeatures(const QOpenGLConfig::Gpu &gpu,
451  const QString &osName,
452  const QVersionNumber &kernelVersion,
453  const QString &osRelease,
454  const QString &fileName,
455  QSet<QString> *result, QString *errorMessage)
456 {
457  result->clear();
458  errorMessage->clear();
460  if (!file.open(QIODevice::ReadOnly)) {
461  QTextStream str(errorMessage);
462  str << "Cannot open \"" << QDir::toNativeSeparators(fileName) << "\": "
463  << file.errorString();
464  return false;
465  }
466  const bool success = readGpuFeatures(gpu, osName, kernelVersion, osRelease, file.readAll(), result, errorMessage);
467  if (!success) {
468  errorMessage->prepend(QLatin1String("Error reading \"")
470  + QLatin1String("\": "));
471  }
472  return success;
473 }
474 
476  const QString &osName,
477  const QVersionNumber &kernelVersion,
478  const QString &osRelease,
479  const QJsonDocument &doc)
480 {
482  QString errorMessage;
483  if (!readGpuFeatures(gpu, osName, kernelVersion, osRelease, doc, &result, &errorMessage))
484  qWarning().noquote() << errorMessage;
485  return result;
486 }
487 
489  const QString &osName,
490  const QVersionNumber &kernelVersion,
491  const QString &osRelease,
492  const QString &fileName)
493 {
495  QString errorMessage;
496  if (!readGpuFeatures(gpu, osName, kernelVersion, osRelease, fileName, &result, &errorMessage))
497  qWarning().noquote() << errorMessage;
498  return result;
499 }
500 
502 {
503  return gpuFeatures(gpu, OsTypeTerm::hostOs(), OsTypeTerm::hostKernelVersion(), OsTypeTerm::hostOsRelease(), doc);
504 }
505 
507 {
508  return gpuFeatures(gpu, OsTypeTerm::hostOs(), OsTypeTerm::hostKernelVersion(), OsTypeTerm::hostOsRelease(), fileName);
509 }
510 
512 {
516  if (!ctx) {
517  tmpContext.reset(new QOpenGLContext);
518  if (!tmpContext->create()) {
519  qWarning("QOpenGLConfig::Gpu::fromContext: Failed to create temporary context");
520  return QOpenGLConfig::Gpu();
521  }
522  tmpSurface.reset(new QOffscreenSurface);
523  tmpSurface->setFormat(tmpContext->format());
524  tmpSurface->create();
525  tmpContext->makeCurrent(tmpSurface.data());
526  }
527 
528  QOpenGLConfig::Gpu gpu;
530  const GLubyte *p = ctx->functions()->glGetString(GL_VENDOR);
531  if (p)
532  gpu.glVendor = QByteArray(reinterpret_cast<const char *>(p));
533 
534  return gpu;
535 }
536 
small capitals from c petite p scientific i
[1]
Definition: afcover.h:80
#define value
[5]
FT_Error error
Definition: cffdrivr.c:657
The QByteArray class provides an array of bytes.
Definition: qbytearray.h:85
QList< QByteArray > split(char sep) const
bool contains(char c) const
Definition: qbytearray.h:565
bool isEmpty() const noexcept
Definition: qbytearray.h:129
QByteArray left(qsizetype len) const
qsizetype count(char c) const
operator<<(QDataStream &ds, qfloat16 f)
Definition: qfloat16.cpp:327
The QDebug class provides an output stream for debugging information.
Definition: qdebug.h:65
Convenience class for custom QDebug operators.
Definition: qdebug.h:176
static QString toNativeSeparators(const QString &pathName)
Definition: qdir.cpp:916
bool isNull() const
Definition: qdom.cpp:2124
The QFile class provides an interface for reading from and writing to files.
Definition: qfile.h:94
bool open(OpenMode flags) override
Definition: qfile.cpp:897
QByteArray readAll()
Definition: qiodevice.cpp:1268
QString errorString() const
Definition: qiodevice.cpp:2169
The QJsonArray::const_iterator class provides an STL-style const iterator for QJsonArray.
Definition: qjsonarray.h:169
The QJsonArray class encapsulates a JSON array.
Definition: qjsonarray.h:54
void insert(qsizetype i, const QJsonValue &value)
Definition: qjsonarray.cpp:431
const_iterator constBegin() const
Definition: qjsonarray.h:225
const_iterator constEnd() const
Definition: qjsonarray.h:229
The QJsonDocument class provides a way to read and write JSON documents.
Definition: qjsondocument.h:83
QJsonObject object() const
static QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error=nullptr)
The QJsonObject class encapsulates a JSON object.
Definition: qjsonobject.h:56
QJsonValue value(const QString &key) const
The QJsonValue class encapsulates a value in JSON.
Definition: qjsonvalue.h:60
bool isString() const
Definition: qjsonvalue.h:110
QJsonArray toArray() const
Definition: qjsonvalue.cpp:754
bool isArray() const
Definition: qjsonvalue.h:111
Type type() const
Definition: qjsonvalue.cpp:608
QString toString() const
Definition: qjsonvalue.cpp:727
The QLatin1String class provides a thin wrapper around an US-ASCII/Latin-1 encoded string literal.
Definition: qstring.h:84
The QOffscreenSurface class represents an offscreen surface in the underlying platform.
void setFormat(const QSurfaceFormat &format)
static QSet< QString > gpuFeatures(const Gpu &gpu, const QString &osName, const QVersionNumber &kernelVersion, const QString &osVersion, const QJsonDocument &doc)
Definition: qopengl.cpp:475
The QOpenGLContext class represents a native OpenGL context, enabling OpenGL rendering on a QSurface.
QFunctionPointer getProcAddress(const QByteArray &procName) const
bool makeCurrent(QSurface *surface)
QSurfaceFormat format() const
static QOpenGLContext * currentContext()
QOpenGLFunctions * functions() const
bool isOpenGLES() const
QSet< QByteArray > extensions() const
Definition: qopengl_p.h:75
The QOpenGLFunctions class provides cross-platform access to the OpenGL ES 2.0 API.
const GLubyte * glGetString(GLenum name)
void glGetIntegerv(GLenum pname, GLint *params)
static QOperatingSystemVersion current()
[0]
T * data() const noexcept
void reset(T *other=nullptr) noexcept(noexcept(Cleanup::cleanup(std::declval< T * >())))
const_iterator constBegin() const noexcept
Definition: qset.h:175
const_iterator constEnd() const noexcept
Definition: qset.h:179
iterator insert(const T &value)
Definition: qset.h:191
The QString class provides a Unicode character string.
Definition: qstring.h:388
QString & prepend(QChar c)
Definition: qstring.h:656
void clear()
Definition: qstring.h:1240
uint toUInt(bool *ok=nullptr, int base=10) const
Definition: qstring.h:850
bool isEmpty() const
Definition: qstring.h:1216
QByteArray toUtf8() const &
Definition: qstring.h:749
int majorVersion() const
static QString kernelVersion()
Definition: qglobal.cpp:2742
The QTextStream class provides a convenient interface for reading and writing text.
Definition: qtextstream.h:62
The QVersionNumber class contains a version number with an arbitrary number of segments.
bool isNull() const noexcept
static Q_CORE_EXPORT QVersionNumber fromString(const QString &string, int *suffixIndex=nullptr)
QString str
[2]
QDomDocument document
[0]
set contains("Julia")
StdString::EqualsMatcher Equals(std::string const &str, CaseSensitive::Choice caseSensitivity=CaseSensitive::Yes)
void toString(QString &appendTo, IPv4Address address)
Definition: qipaddress.cpp:131
const PluginKeyMapConstIterator cend
QTextStream & hex(QTextStream &stream)
QTextStream & showbase(QTextStream &stream)
#define QString()
Definition: parse-defines.h:51
set set set set set set set macro pixldst1 op
typedef QByteArray(EGLAPIENTRYP PFNQGSGETDISPLAYSPROC)()
#define qWarning
Definition: qlogging.h:179
QT_BEGIN_NAMESPACE typedef const GLubyte *QOPENGLF_APIENTRYP qt_glGetStringi(GLenum, GLuint)
QJsonArray::ConstIterator JsonArrayConstIt
Definition: qopengl.cpp:148
#define GL_CONTEXT_LOST
Definition: qopengl.cpp:64
#define QOPENGLF_APIENTRYP
Definition: qopengl.h:301
GLenum type
Definition: qopengl.h:270
typedef GLint(GL_APIENTRYP PFNGLGETPROGRAMRESOURCELOCATIONINDEXEXTPROC)(GLuint program
GLsizei const GLfloat * v
[13]
typedef GLenum(GL_APIENTRYP PFNGLGETGRAPHICSRESETSTATUSKHRPROC)(void)
GLboolean GLboolean g
typedef GLuint(GL_APIENTRYP PFNGLGETDEBUGMESSAGELOGKHRPROC)(GLuint count
GLuint64EXT * result
[6]
Definition: qopenglext.h:10932
GLdouble s
[6]
Definition: qopenglext.h:235
GLfloat GLfloat p
[1]
Definition: qopenglext.h:12698
#define GL_NUM_EXTENSIONS
Definition: qopenglext.h:906
#define QStringLiteral(str)
QByteArray ba
[0]
QFile file
[0]
sem release()
Definition: main.cpp:48
QSharedPointer< T > other(t)
[5]
QStringList::Iterator it
The QJsonParseError class is used to report errors during JSON parsing.
Definition: qjsondocument.h:56
static Gpu fromContext()
Definition: qopengl.cpp:511
QVersionNumber driverVersion
Definition: qopengl_p.h:94
QByteArray glVendor
Definition: qopengl_p.h:96
QByteArray driverDescription
Definition: qopengl_p.h:95