Qt 4.8
qglshadercache_meego_p.h
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the QtOpenGL 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 Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 //
43 // W A R N I N G
44 // -------------
45 //
46 // This file is not part of the Qt API. It exists purely as an
47 // implementation detail. This header file may change from version to
48 // version without notice, or even be removed.
49 //
50 // We mean it.
51 //
52 
53 #ifndef QGLSHADERCACHE_MEEGO_P_H
54 #define QGLSHADERCACHE_MEEGO_P_H
55 
56 #include <QtCore/qglobal.h>
57 
58 #if defined(QT_MEEGO_EXPERIMENTAL_SHADERCACHE) && defined(QT_OPENGL_ES_2)
59 
60 #include <QtCore/qcryptographichash.h>
61 #include <QtCore/qsharedmemory.h>
62 #include <QtCore/qsystemsemaphore.h>
63 
64 #ifndef QT_BOOTSTRAPPED
65 # include <GLES2/gl2ext.h>
66 #endif
67 #if defined(QT_DEBUG) || defined(QT_MEEGO_EXPERIMENTAL_SHADERCACHE_TRACE)
68 # include <syslog.h>
69 #endif
70 
72 
73 /*
74  This cache stores internal Qt shader programs in shared memory.
75 
76  This header file is ugly on purpose and can only be included once. It is only to be used
77  for the internal shader cache, not as a generic cache for anyone's shaders.
78 
79  The cache stores either ShaderCacheMaxEntries shader programs or ShaderCacheDataSize kilobytes
80  of shader programs, whatever limit is reached first.
81 
82  The layout of the cache is as outlined in the CachedShaders struct. After some
83  integers, an array of headers is reserved, then comes the space for the actual binaries.
84 
85  Shader Programs are identified by the md5sum of their frag and vertex shader source code.
86 
87  Shader Programs are never removed. The cache never shrinks or re-shuffles. This is done
88  on purpose to ensure minimum amount of locking, no alignment problems and very few write
89  operations.
90 
91  Note: Locking the shader cache could be expensive, because the entire system might hang.
92  That's why the cache is immutable to minimize the time we need to keep it locked.
93 
94  Why is it Meego specific?
95 
96  First, the size is chosen so that it fits to generic meego usage. Second, on Meego, there's
97  always at least one Qt application active (the launcher), so the cache will never be destroyed.
98  Only when the last Qt app exits, the cache dies, which should only be when someone kills the
99  X11 server. And last but not least it was only tested with Meego's SGX driver.
100 
101  There's a small tool in src/opengl/util/meego that dumps the contents of the cache.
102  */
103 
104 // anonymous namespace, prevent exporting of the private symbols
105 namespace
106 {
107 
108 struct CachedShaderHeader
109 {
110  /* the index in the data[] member of CachedShaders */
111  int index;
112  /* the size of the binary shader */
113  GLsizei size;
114  /* the format of the binary shader */
115  GLenum format;
116  /* the md5sum of the frag+vertex shaders */
117  char md5Sum[16];
118 };
119 
120 enum
121 {
122  /* The maximum amount of shader programs the cache can hold */
123  ShaderCacheMaxEntries = 20
124 };
125 
126 typedef CachedShaderHeader CachedShaderHeaders[ShaderCacheMaxEntries];
127 
128 enum
129 {
130  // ShaderCacheDataSize is 20k minus the other data members of CachedShaders
131  ShaderCacheDataSize = 1024 * ShaderCacheMaxEntries - sizeof(CachedShaderHeaders) - 2 * sizeof(int)
132 };
133 
134 struct CachedShaders
135 {
136  /* How much space is still available in the cache */
137  inline int availableSize() const { return ShaderCacheDataSize - dataSize; }
138 
139  /* The current amount of cached shaders */
140  int shaderCount;
141 
142  /* The current amount (in bytes) of cached data */
143  int dataSize;
144 
145  /* The headers describing the shaders */
146  CachedShaderHeaders headers;
147 
148  /* The actual binary data of the shader programs */
149  char data[ShaderCacheDataSize];
150 };
151 
152 //#define QT_DEBUG_SHADER_CACHE
153 #ifdef QT_DEBUG_SHADER_CACHE
154 static QDebug shaderCacheDebug()
155 {
156  return QDebug(QtDebugMsg);
157 }
158 #else
159 static inline QNoDebug shaderCacheDebug() { return QNoDebug(); }
160 #endif
161 
162 class ShaderCacheSharedMemory
163 {
164 public:
165  ShaderCacheSharedMemory()
166  : shm(QLatin1String("qt_gles2_shadercache_" QT_VERSION_STR))
167  {
168  // we need a system semaphore here, since cache creation and initialization must be atomic
169  QSystemSemaphore attachSemaphore(QLatin1String("qt_gles2_shadercache_mutex_" QT_VERSION_STR), 1);
170 
171  if (!attachSemaphore.acquire()) {
172  shaderCacheDebug() << "Unable to require shader cache semaphore:" << attachSemaphore.errorString();
173  return;
174  }
175 
176  if (shm.attach()) {
177  // success!
178  shaderCacheDebug() << "Attached to shader cache";
179  } else {
180 
181  // no cache exists - create and initialize it
182  if (shm.create(sizeof(CachedShaders))) {
183  shaderCacheDebug() << "Created new shader cache";
184  initializeCache();
185  } else {
186  shaderCacheDebug() << "Unable to create shader cache:" << shm.errorString();
187  }
188  }
189 
190  attachSemaphore.release();
191  }
192 
193  inline bool isAttached() const { return shm.isAttached(); }
194 
195  inline bool lock() { return shm.lock(); }
196  inline bool unlock() { return shm.unlock(); }
197  inline void *data() { return shm.data(); }
198  inline QString errorString() { return shm.errorString(); }
199 
200  ~ShaderCacheSharedMemory()
201  {
202  if (!shm.detach())
203  shaderCacheDebug() << "Unable to detach shader cache" << shm.errorString();
204  }
205 
206 private:
207  void initializeCache()
208  {
209  // no need to lock the shared memory since we're already protected by the
210  // attach system semaphore.
211 
212  void *data = shm.data();
213  Q_ASSERT(data);
214 
215  memset(data, 0, sizeof(CachedShaders));
216  }
217 
218  QSharedMemory shm;
219 };
220 
221 class ShaderCacheLocker
222 {
223 public:
224  inline ShaderCacheLocker(ShaderCacheSharedMemory *cache)
225  : shm(cache->lock() ? cache : (ShaderCacheSharedMemory *)0)
226  {
227  if (!shm)
228  shaderCacheDebug() << "Unable to lock shader cache" << cache->errorString();
229  }
230 
231  inline bool isLocked() const { return shm; }
232 
233  inline ~ShaderCacheLocker()
234  {
235  if (!shm)
236  return;
237  if (!shm->unlock())
238  shaderCacheDebug() << "Unable to unlock shader cache" << shm->errorString();
239  }
240 
241 private:
242  ShaderCacheSharedMemory *shm;
243 };
244 
245 #ifdef QT_BOOTSTRAPPED
246 } // end namespace
247 #else
248 
249 static void traceCacheOverflow(const char *message)
250 {
251 #if defined(QT_DEBUG) || defined (QT_MEEGO_EXPERIMENTAL_SHADERCACHE_TRACE)
252  openlog(qPrintable(QCoreApplication::applicationName()), LOG_PID | LOG_ODELAY, LOG_USER);
253  syslog(LOG_DEBUG, message);
254  closelog();
255 #endif
256  shaderCacheDebug() << message;
257 }
258 
259 Q_GLOBAL_STATIC(ShaderCacheSharedMemory, shaderCacheSharedMemory)
260 
261 /*
262  Finds the index of the shader program identified by md5Sum in the cache.
263  Note: Does NOT lock the cache for reading, the cache must already be locked!
264 
265  Returns -1 when no shader was found.
266  */
267 static int qt_cache_index_unlocked(const QByteArray &md5Sum, CachedShaders *cache)
268 {
269  for (int i = 0; i < cache->shaderCount; ++i) {
270  if (qstrncmp(md5Sum.constData(), cache->headers[i].md5Sum, 16) == 0) {
271  return i;
272  }
273  }
274  return -1;
275 }
276 
277 /* Returns the index of the shader identified by md5Sum */
278 static int qt_cache_index(const QByteArray &md5Sum)
279 {
280  ShaderCacheSharedMemory *shm = shaderCacheSharedMemory();
281  if (!shm || !shm->isAttached())
282  return false;
283 
284  Q_ASSERT(md5Sum.length() == 16);
285 
286  ShaderCacheLocker locker(shm);
287  if (!locker.isLocked())
288  return false;
289 
290  void *data = shm->data();
291  Q_ASSERT(data);
292 
293  CachedShaders *cache = reinterpret_cast<CachedShaders *>(data);
294 
295  return qt_cache_index_unlocked(md5Sum, cache);
296 }
297 
298 /* Loads the cached shader at index \a shaderIndex into \a program
299  * Note: Since the cache is immutable, this operation doesn't lock the shared memory.
300  */
301 static bool qt_cached_shader(QGLShaderProgram *program, const QGLContext *ctx, int shaderIndex)
302 {
303  Q_ASSERT(shaderIndex >= 0 && shaderIndex <= ShaderCacheMaxEntries);
304  Q_ASSERT(program);
305 
306  ShaderCacheSharedMemory *shm = shaderCacheSharedMemory();
307  if (!shm || !shm->isAttached())
308  return false;
309 
310  void *data = shm->data();
311  Q_ASSERT(data);
312 
313  CachedShaders *cache = reinterpret_cast<CachedShaders *>(data);
314 
315  shaderCacheDebug() << "fetching cached shader at index" << shaderIndex
316  << "dataIndex" << cache->headers[shaderIndex].index
317  << "size" << cache->headers[shaderIndex].size
318  << "format" << cache->headers[shaderIndex].format;
319 
320  // call program->programId first, since that resolves the glProgramBinaryOES symbol
321  GLuint programId = program->programId();
322  glProgramBinaryOES(programId, cache->headers[shaderIndex].format,
323  cache->data + cache->headers[shaderIndex].index,
324  cache->headers[shaderIndex].size);
325 
326  return true;
327 }
328 
329 /* Stores the shader program in the cache. Returns false if there's an error with the cache, or
330  if the cache is too small to hold the shader. */
331 static bool qt_cache_shader(const QGLShaderProgram *shader, const QGLContext *ctx, const QByteArray &md5Sum)
332 {
333  ShaderCacheSharedMemory *shm = shaderCacheSharedMemory();
334  if (!shm || !shm->isAttached())
335  return false;
336 
337  void *data = shm->data();
338  Q_ASSERT(data);
339 
340  CachedShaders *cache = reinterpret_cast<CachedShaders *>(data);
341 
342  ShaderCacheLocker locker(shm);
343  if (!locker.isLocked())
344  return false;
345 
346  int cacheIdx = cache->shaderCount;
347  if (cacheIdx >= ShaderCacheMaxEntries) {
348  traceCacheOverflow("Qt OpenGL shader cache index overflow!");
349  return false;
350  }
351 
352  // now that we have the lock on the shared memory, make sure no one
353  // inserted the shader already while we were unlocked
354  if (qt_cache_index_unlocked(md5Sum, cache) != -1)
355  return true; // already cached
356 
357  shaderCacheDebug() << "Caching shader at index" << cacheIdx;
358 
359  GLint binaryLength = 0;
360  glGetProgramiv(shader->programId(), GL_PROGRAM_BINARY_LENGTH_OES, &binaryLength);
361 
362  if (!binaryLength) {
363  shaderCacheDebug() << "Unable to determine binary shader size!";
364  return false;
365  }
366 
367  if (binaryLength > cache->availableSize()) {
368  traceCacheOverflow("Qt OpenGL shader cache data overflow!");
369  return false;
370  }
371 
372  GLsizei size = 0;
373  GLenum format = 0;
374  glGetProgramBinaryOES(shader->programId(), binaryLength, &size, &format,
375  cache->data + cache->dataSize);
376 
377  if (!size) {
378  shaderCacheDebug() << "Unable to get binary shader!";
379  return false;
380  }
381 
382  cache->headers[cacheIdx].index = cache->dataSize;
383  cache->dataSize += binaryLength;
384  ++cache->shaderCount;
385  cache->headers[cacheIdx].size = binaryLength;
386  cache->headers[cacheIdx].format = format;
387 
388  memcpy(cache->headers[cacheIdx].md5Sum, md5Sum.constData(), 16);
389 
390  shaderCacheDebug() << "cached shader size" << size
391  << "format" << format
392  << "binarySize" << binaryLength
393  << "cache index" << cacheIdx
394  << "data index" << cache->headers[cacheIdx].index;
395 
396  return true;
397 }
398 
399 } // namespace
400 
402 
404 
405 class CachedShader
406 {
407 public:
408  CachedShader(const QByteArray &fragSource, const QByteArray &vertexSource)
409  : cacheIdx(-1)
410  {
412 
413  md5Hash.addData(fragSource);
414  md5Hash.addData(vertexSource);
415 
416  md5Sum = md5Hash.result();
417  }
418 
419  bool isCached()
420  {
421  return cacheIndex() != -1;
422  }
423 
424  int cacheIndex()
425  {
426  if (cacheIdx != -1)
427  return cacheIdx;
428  cacheIdx = qt_cache_index(md5Sum);
429  return cacheIdx;
430  }
431 
432  bool load(QGLShaderProgram *program, const QGLContext *ctx)
433  {
434  if (cacheIndex() == -1)
435  return false;
436  return qt_cached_shader(program, ctx, cacheIdx);
437  }
438 
439  bool store(QGLShaderProgram *program, const QGLContext *ctx)
440  {
441  return qt_cache_shader(program, ctx, md5Sum);
442  }
443 
444 private:
445  QByteArray md5Sum;
446  int cacheIdx;
447 };
448 
449 
451 
452 #endif
453 
455 
456 #endif
457 #endif
The QDebug class provides an output stream for debugging information.
Definition: qdebug.h:62
#define QT_END_NAMESPACE
This macro expands to.
Definition: qglobal.h:90
#define QT_MODULE(x)
Definition: qglobal.h:2783
GLuint programId() const
Returns the OpenGL identifier associated with this shader program.
#define QT_BEGIN_HEADER
Definition: qglobal.h:136
void unlock()
Unlocks the lock.
The QByteArray class provides an array of bytes.
Definition: qbytearray.h:135
The QSystemSemaphore class provides a general counting system semaphore.
QLatin1String(DBUS_INTERFACE_DBUS))) Q_GLOBAL_STATIC_WITH_ARGS(QString
The QString class provides a Unicode character string.
Definition: qstring.h:83
#define Q_ASSERT(cond)
Definition: qglobal.h:1823
Q_GUI_EXPORT QString errorString(EGLint code=eglGetError())
Definition: qegl.cpp:743
#define QT_BEGIN_NAMESPACE
This macro expands to.
Definition: qglobal.h:89
The QGLContext class encapsulates an OpenGL rendering context.
Definition: qgl.h:310
#define Q_GLOBAL_STATIC(TYPE, NAME)
Declares a global static variable with the given type and name.
Definition: qglobal.h:1968
static const char * data(const QByteArray &arr)
#define QT_VERSION_STR
This macro expands to a string that specifies Qt&#39;s version number (for example, "4.
Definition: qglobal.h:47
The QLatin1String class provides a thin wrapper around an US-ASCII/Latin-1 encoded string literal...
Definition: qstring.h:654
#define glGetProgramiv
int length() const
Same as size().
Definition: qbytearray.h:356
const char * constData() const
Returns a pointer to the data stored in the byte array.
Definition: qbytearray.h:433
unsigned int GLenum
Definition: main.cpp:50
#define load(x)
#define store(x)
#define ctx
Definition: qgl.cpp:6094
int qstrncmp(const char *str1, const char *str2, uint len)
Definition: qbytearray.h:101
static QReadWriteLock lock
Definition: proxyconf.cpp:399
The QSharedMemory class provides access to a shared memory segment.
Definition: qsharedmemory.h:57
quint16 index
int GLsizei
Definition: main.cpp:49
typedef GLint
Definition: glfunctions.h:67
static QString applicationName()
#define qPrintable(string)
Definition: qglobal.h:1750
#define QT_END_HEADER
Definition: qglobal.h:137
The QGLShaderProgram class allows OpenGL shader programs to be linked and used.
The QCryptographicHash class provides a way to generate cryptographic hashes.