QtBase  v6.3.1
qopenglprogrambinarycache.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 
41 #include <QOpenGLContext>
42 #include <QOpenGLExtraFunctions>
43 #include <QSysInfo>
44 #include <QStandardPaths>
45 #include <QDir>
46 #include <QSaveFile>
47 #include <QCoreApplication>
48 #include <QCryptographicHash>
49 
50 #ifdef Q_OS_UNIX
51 #include <sys/mman.h>
52 #include <private/qcore_unix_p.h>
53 #endif
54 
56 
57 Q_LOGGING_CATEGORY(lcOpenGLProgramDiskCache, "qt.opengl.diskcache")
58 
59 #ifndef GL_CONTEXT_LOST
60 #define GL_CONTEXT_LOST 0x0507
61 #endif
62 
63 #ifndef GL_PROGRAM_BINARY_LENGTH
64 #define GL_PROGRAM_BINARY_LENGTH 0x8741
65 #endif
66 
67 #ifndef GL_NUM_PROGRAM_BINARY_FORMATS
68 #define GL_NUM_PROGRAM_BINARY_FORMATS 0x87FE
69 #endif
70 
71 const quint32 BINSHADER_MAGIC = 0x5174;
74 
75 namespace {
76 struct GLEnvInfo
77 {
78  GLEnvInfo();
79 
80  QByteArray glvendor;
81  QByteArray glrenderer;
82  QByteArray glversion;
83 };
84 }
85 
86 GLEnvInfo::GLEnvInfo()
87 {
89  Q_ASSERT(ctx);
90  QOpenGLFunctions *f = ctx->functions();
91  const char *vendor = reinterpret_cast<const char *>(f->glGetString(GL_VENDOR));
92  const char *renderer = reinterpret_cast<const char *>(f->glGetString(GL_RENDERER));
93  const char *version = reinterpret_cast<const char *>(f->glGetString(GL_VERSION));
94  if (vendor)
95  glvendor = QByteArray(vendor);
96  if (renderer)
97  glrenderer = QByteArray(renderer);
98  if (version)
99  glversion = QByteArray(version);
100 }
101 
102 QByteArray QOpenGLProgramBinaryCache::ProgramDesc::cacheKey() const
103 {
105  for (const QOpenGLProgramBinaryCache::ShaderDesc &shader : shaders)
106  keyBuilder.addData(shader.source);
107 
108  return keyBuilder.result().toHex();
109 }
110 
111 static inline bool qt_ensureWritableDir(const QString &name)
112 {
114  return QFileInfo(name).isWritable();
115 }
116 
117 QOpenGLProgramBinaryCache::QOpenGLProgramBinaryCache()
118  : m_cacheWritable(false)
119 {
120  const QString subPath = QLatin1String("/qtshadercache-") + QSysInfo::buildAbi() + QLatin1Char('/');
122  m_globalCacheDir = sharedCachePath + subPath;
124 
125  if (!sharedCachePath.isEmpty()) {
126  m_currentCacheDir = m_globalCacheDir;
127  m_cacheWritable = qt_ensureWritableDir(m_currentCacheDir);
128  }
129  if (!m_cacheWritable) {
130  m_currentCacheDir = m_localCacheDir;
131  m_cacheWritable = qt_ensureWritableDir(m_currentCacheDir);
132  }
133 
134  qCDebug(lcOpenGLProgramDiskCache, "Cache location '%s' writable = %d", qPrintable(m_currentCacheDir), m_cacheWritable);
135 }
136 
137 QString QOpenGLProgramBinaryCache::cacheFileName(const QByteArray &cacheKey) const
138 {
139  return m_currentCacheDir + QString::fromUtf8(cacheKey);
140 }
141 
142 #define BASE_HEADER_SIZE (int(4 * sizeof(quint32)))
143 #define FULL_HEADER_SIZE(stringsSize) (BASE_HEADER_SIZE + 12 + stringsSize + 8)
144 #define PADDING_SIZE(fullHeaderSize) (((fullHeaderSize + 3) & ~3) - fullHeaderSize)
145 
146 static inline quint32 readUInt(const uchar **p)
147 {
148  quint32 v;
149  memcpy(&v, *p, sizeof(quint32));
150  *p += sizeof(quint32);
151  return v;
152 }
153 
154 static inline QByteArray readStr(const uchar **p)
155 {
156  quint32 len = readUInt(p);
157  QByteArray ba = QByteArray::fromRawData(reinterpret_cast<const char *>(*p), len);
158  *p += len;
159  return ba;
160 }
161 
162 bool QOpenGLProgramBinaryCache::verifyHeader(const QByteArray &buf) const
163 {
164  if (buf.size() < BASE_HEADER_SIZE) {
165  qCDebug(lcOpenGLProgramDiskCache, "Cached size too small");
166  return false;
167  }
168  const uchar *p = reinterpret_cast<const uchar *>(buf.constData());
169  if (readUInt(&p) != BINSHADER_MAGIC) {
170  qCDebug(lcOpenGLProgramDiskCache, "Magic does not match");
171  return false;
172  }
173  if (readUInt(&p) != BINSHADER_VERSION) {
174  qCDebug(lcOpenGLProgramDiskCache, "Version does not match");
175  return false;
176  }
177  if (readUInt(&p) != BINSHADER_QTVERSION) {
178  qCDebug(lcOpenGLProgramDiskCache, "Qt version does not match");
179  return false;
180  }
181  if (readUInt(&p) != sizeof(quintptr)) {
182  qCDebug(lcOpenGLProgramDiskCache, "Architecture does not match");
183  return false;
184  }
185  return true;
186 }
187 
188 bool QOpenGLProgramBinaryCache::setProgramBinary(uint programId, uint blobFormat, const void *p, uint blobSize)
189 {
191  QOpenGLExtraFunctions *funcs = context->extraFunctions();
192  while (true) {
193  GLenum error = funcs->glGetError();
194  if (error == GL_NO_ERROR || error == GL_CONTEXT_LOST)
195  break;
196  }
197 #if QT_CONFIG(opengles2)
198  if (context->isOpenGLES() && context->format().majorVersion() < 3) {
199  initializeProgramBinaryOES(context);
200  programBinaryOES(programId, blobFormat, p, blobSize);
201  } else
202 #endif
203  funcs->glProgramBinary(programId, blobFormat, p, blobSize);
204 
205  GLenum err = funcs->glGetError();
206  if (err != GL_NO_ERROR) {
207  qCDebug(lcOpenGLProgramDiskCache, "Program binary failed to load for program %u, size %d, "
208  "format 0x%x, err = 0x%x",
209  programId, blobSize, blobFormat, err);
210  return false;
211  }
212  GLint linkStatus = 0;
213  funcs->glGetProgramiv(programId, GL_LINK_STATUS, &linkStatus);
214  if (linkStatus != GL_TRUE) {
215  qCDebug(lcOpenGLProgramDiskCache, "Program binary failed to load for program %u, size %d, "
216  "format 0x%x, linkStatus = 0x%x, err = 0x%x",
217  programId, blobSize, blobFormat, linkStatus, err);
218  return false;
219  }
220 
221  qCDebug(lcOpenGLProgramDiskCache, "Program binary set for program %u, size %d, format 0x%x, err = 0x%x",
222  programId, blobSize, blobFormat, err);
223  return true;
224 }
225 
226 #ifdef Q_OS_UNIX
227 class FdWrapper
228 {
229 public:
230  FdWrapper(const QString &fn)
231  : ptr(MAP_FAILED)
232  {
233  fd = qt_safe_open(QFile::encodeName(fn).constData(), O_RDONLY);
234  }
235  ~FdWrapper()
236  {
237  if (ptr != MAP_FAILED)
238  munmap(ptr, mapSize);
239  if (fd != -1)
240  qt_safe_close(fd);
241  }
242  bool map()
243  {
244  off_t offs = lseek(fd, 0, SEEK_END);
245  if (offs == (off_t) -1) {
246  qErrnoWarning(errno, "lseek failed for program binary");
247  return false;
248  }
249  mapSize = static_cast<size_t>(offs);
250  ptr = mmap(nullptr, mapSize, PROT_READ, MAP_SHARED, fd, 0);
251  return ptr != MAP_FAILED;
252  }
253 
254  int fd;
255  void *ptr;
256  size_t mapSize;
257 };
258 #endif
259 
261 {
262 public:
264  : fn(fn),
265  active(false)
266  {
267  }
269  {
270  if (active)
271  QFile(fn).remove();
272  }
273  void setActive()
274  {
275  active = true;
276  }
277 
279  bool active;
280 };
281 
282 bool QOpenGLProgramBinaryCache::load(const QByteArray &cacheKey, uint programId)
283 {
284  QMutexLocker lock(&m_mutex);
285  if (const MemCacheEntry *e = m_memCache.object(cacheKey))
286  return setProgramBinary(programId, e->format, e->blob.constData(), e->blob.size());
287 
288  QByteArray buf;
289  const QString fn = cacheFileName(cacheKey);
290  DeferredFileRemove undertaker(fn);
291 #ifdef Q_OS_UNIX
292  FdWrapper fdw(fn);
293  if (fdw.fd == -1)
294  return false;
295  char header[BASE_HEADER_SIZE];
296  qint64 bytesRead = qt_safe_read(fdw.fd, header, BASE_HEADER_SIZE);
297  if (bytesRead == BASE_HEADER_SIZE)
299 #else
300  QFile f(fn);
301  if (!f.open(QIODevice::ReadOnly))
302  return false;
303  buf = f.read(BASE_HEADER_SIZE);
304 #endif
305 
306  if (!verifyHeader(buf)) {
307  undertaker.setActive();
308  return false;
309  }
310 
311  const uchar *p;
312 #ifdef Q_OS_UNIX
313  if (!fdw.map()) {
314  undertaker.setActive();
315  return false;
316  }
317  p = static_cast<const uchar *>(fdw.ptr) + BASE_HEADER_SIZE;
318 #else
319  buf = f.readAll();
320  p = reinterpret_cast<const uchar *>(buf.constData());
321 #endif
322 
323  GLEnvInfo info;
324 
325  QByteArray vendor = readStr(&p);
326  if (vendor != info.glvendor) {
327  // readStr returns non-null terminated strings just pointing to inside
328  // 'p' so must print these via the stream qCDebug and not constData().
329  qCDebug(lcOpenGLProgramDiskCache) << "GL_VENDOR does not match" << vendor << info.glvendor;
330  undertaker.setActive();
331  return false;
332  }
333  QByteArray renderer = readStr(&p);
334  if (renderer != info.glrenderer) {
335  qCDebug(lcOpenGLProgramDiskCache) << "GL_RENDERER does not match" << renderer << info.glrenderer;
336  undertaker.setActive();
337  return false;
338  }
339  QByteArray version = readStr(&p);
340  if (version != info.glversion) {
341  qCDebug(lcOpenGLProgramDiskCache) << "GL_VERSION does not match" << version << info.glversion;
342  undertaker.setActive();
343  return false;
344  }
345 
346  quint32 blobFormat = readUInt(&p);
347  quint32 blobSize = readUInt(&p);
348 
349  p += PADDING_SIZE(FULL_HEADER_SIZE(vendor.size() + renderer.size() + version.size()));
350 
351  return setProgramBinary(programId, blobFormat, p, blobSize)
352  && m_memCache.insert(cacheKey, new MemCacheEntry(p, blobSize, blobFormat));
353 }
354 
355 static inline void writeUInt(uchar **p, quint32 value)
356 {
357  memcpy(*p, &value, sizeof(quint32));
358  *p += sizeof(quint32);
359 }
360 
361 static inline void writeStr(uchar **p, const QByteArray &str)
362 {
363  writeUInt(p, str.size());
364  memcpy(*p, str.constData(), str.size());
365  *p += str.size();
366 }
367 
368 static inline bool writeFile(const QString &filename, const QByteArray &data)
369 {
370 #if QT_CONFIG(temporaryfile)
373  f.write(data);
374  if (f.commit())
375  return true;
376  }
377 #else
378  QFile f(filename);
380  if (f.write(data) == data.length())
381  return true;
382  }
383 #endif
384  return false;
385 }
386 
387 void QOpenGLProgramBinaryCache::save(const QByteArray &cacheKey, uint programId)
388 {
389  if (!m_cacheWritable)
390  return;
391 
392  GLEnvInfo info;
393 
395  QOpenGLExtraFunctions *funcs = context->extraFunctions();
396  GLint blobSize = 0;
397  while (true) {
398  GLenum error = funcs->glGetError();
399  if (error == GL_NO_ERROR || error == GL_CONTEXT_LOST)
400  break;
401  }
402  funcs->glGetProgramiv(programId, GL_PROGRAM_BINARY_LENGTH, &blobSize);
403 
404  const int headerSize = FULL_HEADER_SIZE(info.glvendor.size() + info.glrenderer.size() + info.glversion.size());
405 
406  // Add padding to make the blob start 4-byte aligned in order to support
407  // OpenGL implementations on ARM that choke on non-aligned pointers passed
408  // to glProgramBinary.
409  const int paddingSize = PADDING_SIZE(headerSize);
410 
411  const int totalSize = headerSize + paddingSize + blobSize;
412 
413  qCDebug(lcOpenGLProgramDiskCache, "Program binary is %d bytes, err = 0x%x, total %d", blobSize, funcs->glGetError(), totalSize);
414  if (!blobSize)
415  return;
416 
417  QByteArray blob(totalSize, Qt::Uninitialized);
418  uchar *p = reinterpret_cast<uchar *>(blob.data());
419 
420  writeUInt(&p, BINSHADER_MAGIC);
421  writeUInt(&p, BINSHADER_VERSION);
422  writeUInt(&p, BINSHADER_QTVERSION);
423  writeUInt(&p, sizeof(quintptr));
424 
425  writeStr(&p, info.glvendor);
426  writeStr(&p, info.glrenderer);
427  writeStr(&p, info.glversion);
428 
429  quint32 blobFormat = 0;
430  uchar *blobFormatPtr = p;
431  writeUInt(&p, blobFormat);
432  writeUInt(&p, blobSize);
433 
434  for (int i = 0; i < paddingSize; ++i)
435  *p++ = 0;
436 
437  GLint outSize = 0;
438 #if QT_CONFIG(opengles2)
439  if (context->isOpenGLES() && context->format().majorVersion() < 3) {
440  QMutexLocker lock(&m_mutex);
441  initializeProgramBinaryOES(context);
442  getProgramBinaryOES(programId, blobSize, &outSize, &blobFormat, p);
443  } else
444 #endif
445  funcs->glGetProgramBinary(programId, blobSize, &outSize, &blobFormat, p);
446  if (blobSize != outSize) {
447  qCDebug(lcOpenGLProgramDiskCache, "glGetProgramBinary returned size %d instead of %d", outSize, blobSize);
448  return;
449  }
450 
451  writeUInt(&blobFormatPtr, blobFormat);
452 
453  QString filename = cacheFileName(cacheKey);
454  bool ok = writeFile(filename, blob);
455  if (!ok && m_currentCacheDir == m_globalCacheDir) {
456  m_currentCacheDir = m_localCacheDir;
457  m_cacheWritable = qt_ensureWritableDir(m_currentCacheDir);
458  qCDebug(lcOpenGLProgramDiskCache, "Cache location changed to '%s' writable = %d",
459  qPrintable(m_currentCacheDir), m_cacheWritable);
460  if (m_cacheWritable) {
461  filename = cacheFileName(cacheKey);
462  ok = writeFile(filename, blob);
463  }
464  }
465  if (!ok)
466  qCDebug(lcOpenGLProgramDiskCache, "Failed to write %s to shader cache", qPrintable(filename));
467 }
468 
469 #if QT_CONFIG(opengles2)
470 void QOpenGLProgramBinaryCache::initializeProgramBinaryOES(QOpenGLContext *context)
471 {
472  if (m_programBinaryOESInitialized)
473  return;
474  m_programBinaryOESInitialized = true;
475 
476  Q_ASSERT(context);
477  getProgramBinaryOES = (void (QOPENGLF_APIENTRYP)(GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, GLvoid *binary))context->getProcAddress("glGetProgramBinaryOES");
478  programBinaryOES = (void (QOPENGLF_APIENTRYP)(GLuint program, GLenum binaryFormat, const GLvoid *binary, GLint length))context->getProcAddress("glProgramBinaryOES");
479 }
480 #endif
481 
483  : QOpenGLSharedResource(context->shareGroup()),
484  m_supported(false)
485 {
487  qCDebug(lcOpenGLProgramDiskCache, "Shader cache disabled via app attribute");
488  return;
489  }
490  if (qEnvironmentVariableIntValue("QT_DISABLE_SHADER_DISK_CACHE")) {
491  qCDebug(lcOpenGLProgramDiskCache, "Shader cache disabled via env var");
492  return;
493  }
494 
496  if (ctx) {
497  if (ctx->isOpenGLES()) {
498  qCDebug(lcOpenGLProgramDiskCache, "OpenGL ES v%d context", ctx->format().majorVersion());
499  if (ctx->format().majorVersion() >= 3) {
500  m_supported = true;
501  } else {
502  const bool hasExt = ctx->hasExtension("GL_OES_get_program_binary");
503  qCDebug(lcOpenGLProgramDiskCache, "GL_OES_get_program_binary support = %d", hasExt);
504  if (hasExt)
505  m_supported = true;
506  }
507  } else {
508  const bool hasExt = ctx->hasExtension("GL_ARB_get_program_binary");
509  qCDebug(lcOpenGLProgramDiskCache, "GL_ARB_get_program_binary support = %d", hasExt);
510  if (hasExt)
511  m_supported = true;
512  }
513  if (m_supported) {
514  GLint fmtCount = 0;
516  qCDebug(lcOpenGLProgramDiskCache, "Supported binary format count = %d", fmtCount);
517  m_supported = fmtCount > 0;
518  }
519  }
520  qCDebug(lcOpenGLProgramDiskCache, "Shader cache supported = %d", m_supported);
521 }
522 
small capitals from c petite p scientific i
[1]
Definition: afcover.h:80
FT_Error error
Definition: cffdrivr.c:657
DeferredFileRemove(const QString &fn)
The QByteArray class provides an array of bytes.
Definition: qbytearray.h:85
qsizetype size() const noexcept
Definition: qbytearray.h:470
static QByteArray fromRawData(const char *data, qsizetype size)
Definition: qbytearray.h:396
static bool testAttribute(Qt::ApplicationAttribute attribute)
The QCryptographicHash class provides a way to generate cryptographic hashes.
static QDir root()
Definition: qdir.h:249
bool mkpath(const QString &dirPath) const
Definition: qdir.cpp:1537
The QFile class provides an interface for reading from and writing to files.
Definition: qfile.h:94
bool remove()
Definition: qfile.cpp:444
static QByteArray encodeName(const QString &fileName)
Definition: qfile.h:159
The QFileInfo class provides system-independent file information.
Definition: qfileinfo.h:57
bool isWritable() const
Definition: qfileinfo.cpp:951
The QLatin1String class provides a thin wrapper around an US-ASCII/Latin-1 encoded string literal.
Definition: qstring.h:84
The QMutexLocker class is a convenience class that simplifies locking and unlocking mutexes.
Definition: qmutex.h:317
The QOpenGLContext class represents a native OpenGL context, enabling OpenGL rendering on a QSurface.
QSurfaceFormat format() const
bool hasExtension(const QByteArray &extension) const
static QOpenGLContext * currentContext()
QOpenGLFunctions * functions() const
bool isOpenGLES() const
The QOpenGLExtraFunctions class provides cross-platform access to the OpenGL ES 3....
void glGetProgramBinary(GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary)
void glProgramBinary(GLuint program, GLenum binaryFormat, const void *binary, GLsizei length)
The QOpenGLFunctions class provides cross-platform access to the OpenGL ES 2.0 API.
void glGetProgramiv(GLuint program, GLenum pname, GLint *params)
void glGetIntegerv(GLenum pname, GLint *params)
QOpenGLProgramBinarySupportCheck(QOpenGLContext *context)
The QSaveFile class provides an interface for safely writing to files.
Definition: qsavefile.h:60
static QString writableLocation(StandardLocation type)
The QString class provides a Unicode character string.
Definition: qstring.h:388
const QChar * constData() const
Definition: qstring.h:1234
qsizetype size() const
Definition: qstring.h:413
static QString fromUtf8(QByteArrayView utf8)
Definition: qstring.cpp:5632
bool isEmpty() const
Definition: qstring.h:1216
int majorVersion() const
static QString buildAbi()
Definition: qglobal.cpp:2680
QMap< QString, QString > map
[6]
QString str
[2]
p1 load("image.bmp")
double e
int const char * version
Definition: zlib.h:814
#define SEEK_END
Definition: ftzconf.h:251
backing_store_ptr info
[4]
Definition: jmemsys.h:161
@ AA_DisableShaderDiskCache
Definition: qnamespace.h:487
constexpr Initialization Uninitialized
Definition: qnamespace.h:1613
void
Definition: png.h:1080
typedef QByteArray(EGLAPIENTRYP PFNQGSGETDISPLAYSPROC)()
EGLOutputLayerEXT EGLint EGLAttrib value
unsigned int quint32
Definition: qglobal.h:288
#define QT_VERSION
Definition: qglobal.h:58
QT_BEGIN_INCLUDE_NAMESPACE typedef unsigned char uchar
Definition: qglobal.h:332
size_t quintptr
Definition: qglobal.h:310
unsigned int uint
Definition: qglobal.h:334
long long qint64
Definition: qglobal.h:298
Q_CORE_EXPORT Q_DECL_COLD_FUNCTION void qErrnoWarning(int code, const char *msg,...)
#define Q_LOGGING_CATEGORY(name,...)
#define qCDebug(category,...)
#define QOPENGLF_APIENTRYP
Definition: qopengl.h:301
GLenum GLuint GLenum GLsizei length
Definition: qopengl.h:270
GLsizei GLsizei GLenum void * binary
typedef GLint(GL_APIENTRYP PFNGLGETPROGRAMRESOURCELOCATIONINDEXEXTPROC)(GLuint program
GLsizei const GLfloat * v
[13]
GLfloat GLfloat f
typedef GLsizei(GL_APIENTRYP PFNGLGETFRAMEBUFFERPIXELLOCALSTORAGESIZEEXTPROC)(GLuint target)
typedef GLenum(GL_APIENTRYP PFNGLGETGRAPHICSRESETSTATUSKHRPROC)(void)
GLenum GLuint GLenum GLsizei const GLchar * buf
GLuint program
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLuint64 GLenum GLint fd
GLsizei GLsizei GLenum * binaryFormat
GLuint name
GLsizei bufSize
typedef GLuint(GL_APIENTRYP PFNGLGETDEBUGMESSAGELOGKHRPROC)(GLuint count
GLuint shader
Definition: qopenglext.h:665
GLenum GLsizei len
Definition: qopenglext.h:3292
GLsizei GLsizei GLuint * shaders
Definition: qopenglext.h:677
GLfloat GLfloat p
[1]
Definition: qopenglext.h:12698
#define GL_LINK_STATUS
Definition: qopenglext.h:638
#define GL_NUM_PROGRAM_BINARY_FORMATS
#define BASE_HEADER_SIZE
const quint32 BINSHADER_VERSION
const quint32 BINSHADER_QTVERSION
#define GL_PROGRAM_BINARY_LENGTH
#define PADDING_SIZE(fullHeaderSize)
const quint32 BINSHADER_MAGIC
#define GL_CONTEXT_LOST
#define FULL_HEADER_SIZE(stringsSize)
#define Q_ASSERT(cond)
Definition: qrandom.cpp:84
#define MAP_FAILED
Definition: qresource.cpp:1170
QByteArray ba
[0]
void writeFile(QCborStreamWriter &writer, const QString &fileName)
[6]
QReadWriteLock lock
[0]
QHttpRequestHeader header("GET", QUrl::toPercentEncoding("/index.html"))
[1]
The QLatin1Char class provides an 8-bit ASCII/Latin-1 character.
Definition: qchar.h:53
int fn(int &i)