diff options
-rw-r--r-- | src/opengl/gl2paintengineex/qglengineshadermanager.cpp | 294 | ||||
-rw-r--r-- | src/opengl/gl2paintengineex/qglshadercache_meego_p.h | 457 | ||||
-rw-r--r-- | src/opengl/gl2paintengineex/qglshadercache_p.h | 98 | ||||
-rw-r--r-- | src/opengl/opengl.pro | 4 | ||||
-rw-r--r-- | src/opengl/util/meego/main.cpp | 48 | ||||
-rw-r--r-- | src/opengl/util/meego/shader-cache-introspector.pro | 7 |
6 files changed, 771 insertions, 137 deletions
diff --git a/src/opengl/gl2paintengineex/qglengineshadermanager.cpp b/src/opengl/gl2paintengineex/qglengineshadermanager.cpp index 93ff3f4..0723c28 100644 --- a/src/opengl/gl2paintengineex/qglengineshadermanager.cpp +++ b/src/opengl/gl2paintengineex/qglengineshadermanager.cpp @@ -42,6 +42,7 @@ #include "qglengineshadermanager_p.h" #include "qglengineshadersource_p.h" #include "qpaintengineex_opengl2_p.h" +#include "qglshadercache_p.h" #if defined(QT_DEBUG) #include <QMetaEnum> @@ -170,64 +171,92 @@ QGLEngineSharedShaders::QGLEngineSharedShaders(const QGLContext* context) QGLShader* fragShader; QGLShader* vertexShader; - QByteArray source; + QByteArray vertexSource; + QByteArray fragSource; // Compile up the simple shader: - source.clear(); - source.append(qShaderSnippets[MainVertexShader]); - source.append(qShaderSnippets[PositionOnlyVertexShader]); - vertexShader = new QGLShader(QGLShader::Vertex, context, 0); - shaders.append(vertexShader); - if (!vertexShader->compileSourceCode(source)) - qWarning("Vertex shader for simpleShaderProg (MainVertexShader & PositionOnlyVertexShader) failed to compile"); - - source.clear(); - source.append(qShaderSnippets[MainFragmentShader]); - source.append(qShaderSnippets[ShockingPinkSrcFragmentShader]); - fragShader = new QGLShader(QGLShader::Fragment, context, 0); - shaders.append(fragShader); - if (!fragShader->compileSourceCode(source)) - qWarning("Fragment shader for simpleShaderProg (MainFragmentShader & ShockingPinkSrcFragmentShader) failed to compile"); + vertexSource.append(qShaderSnippets[MainVertexShader]); + vertexSource.append(qShaderSnippets[PositionOnlyVertexShader]); + + fragSource.append(qShaderSnippets[MainFragmentShader]); + fragSource.append(qShaderSnippets[ShockingPinkSrcFragmentShader]); simpleShaderProg = new QGLShaderProgram(context, 0); - simpleShaderProg->addShader(vertexShader); - simpleShaderProg->addShader(fragShader); - simpleShaderProg->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR); - simpleShaderProg->bindAttributeLocation("pmvMatrix1", QT_PMV_MATRIX_1_ATTR); - simpleShaderProg->bindAttributeLocation("pmvMatrix2", QT_PMV_MATRIX_2_ATTR); - simpleShaderProg->bindAttributeLocation("pmvMatrix3", QT_PMV_MATRIX_3_ATTR); + + CachedShader simpleShaderCache(fragSource, vertexSource); + + bool inCache = simpleShaderCache.load(simpleShaderProg, context); + + if (!inCache) { + vertexShader = new QGLShader(QGLShader::Vertex, context, 0); + shaders.append(vertexShader); + if (!vertexShader->compileSourceCode(vertexSource)) + qWarning("Vertex shader for simpleShaderProg (MainVertexShader & PositionOnlyVertexShader) failed to compile"); + + fragShader = new QGLShader(QGLShader::Fragment, context, 0); + shaders.append(fragShader); + if (!fragShader->compileSourceCode(fragSource)) + qWarning("Fragment shader for simpleShaderProg (MainFragmentShader & ShockingPinkSrcFragmentShader) failed to compile"); + + simpleShaderProg->addShader(vertexShader); + simpleShaderProg->addShader(fragShader); + + simpleShaderProg->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR); + simpleShaderProg->bindAttributeLocation("pmvMatrix1", QT_PMV_MATRIX_1_ATTR); + simpleShaderProg->bindAttributeLocation("pmvMatrix2", QT_PMV_MATRIX_2_ATTR); + simpleShaderProg->bindAttributeLocation("pmvMatrix3", QT_PMV_MATRIX_3_ATTR); + } + simpleShaderProg->link(); - if (!simpleShaderProg->isLinked()) { + + if (simpleShaderProg->isLinked()) { + if (!inCache) + simpleShaderCache.store(simpleShaderProg, context); + } else { qCritical() << "Errors linking simple shader:" << simpleShaderProg->log(); } // Compile the blit shader: - source.clear(); - source.append(qShaderSnippets[MainWithTexCoordsVertexShader]); - source.append(qShaderSnippets[UntransformedPositionVertexShader]); - vertexShader = new QGLShader(QGLShader::Vertex, context, 0); - shaders.append(vertexShader); - if (!vertexShader->compileSourceCode(source)) - qWarning("Vertex shader for blitShaderProg (MainWithTexCoordsVertexShader & UntransformedPositionVertexShader) failed to compile"); - - source.clear(); - source.append(qShaderSnippets[MainFragmentShader]); - source.append(qShaderSnippets[ImageSrcFragmentShader]); - fragShader = new QGLShader(QGLShader::Fragment, context, 0); - shaders.append(fragShader); - if (!fragShader->compileSourceCode(source)) - qWarning("Fragment shader for blitShaderProg (MainFragmentShader & ImageSrcFragmentShader) failed to compile"); + vertexSource.clear(); + vertexSource.append(qShaderSnippets[MainWithTexCoordsVertexShader]); + vertexSource.append(qShaderSnippets[UntransformedPositionVertexShader]); + + fragSource.clear(); + fragSource.append(qShaderSnippets[MainFragmentShader]); + fragSource.append(qShaderSnippets[ImageSrcFragmentShader]); blitShaderProg = new QGLShaderProgram(context, 0); - blitShaderProg->addShader(vertexShader); - blitShaderProg->addShader(fragShader); - blitShaderProg->bindAttributeLocation("textureCoordArray", QT_TEXTURE_COORDS_ATTR); - blitShaderProg->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR); + + CachedShader blitShaderCache(fragSource, vertexSource); + + inCache = blitShaderCache.load(blitShaderProg, context); + + if (!inCache) { + vertexShader = new QGLShader(QGLShader::Vertex, context, 0); + shaders.append(vertexShader); + if (!vertexShader->compileSourceCode(vertexSource)) + qWarning("Vertex shader for blitShaderProg (MainWithTexCoordsVertexShader & UntransformedPositionVertexShader) failed to compile"); + + fragShader = new QGLShader(QGLShader::Fragment, context, 0); + shaders.append(fragShader); + if (!fragShader->compileSourceCode(fragSource)) + qWarning("Fragment shader for blitShaderProg (MainFragmentShader & ImageSrcFragmentShader) failed to compile"); + + blitShaderProg->addShader(vertexShader); + blitShaderProg->addShader(fragShader); + + blitShaderProg->bindAttributeLocation("textureCoordArray", QT_TEXTURE_COORDS_ATTR); + blitShaderProg->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR); + } + blitShaderProg->link(); - if (!blitShaderProg->isLinked()) { + if (blitShaderProg->isLinked()) { + if (!inCache) + blitShaderCache.store(blitShaderProg, context); + } else { qCritical() << "Errors linking blit shader:" - << simpleShaderProg->log(); + << blitShaderProg->log(); } #ifdef QT_GL_SHARED_SHADER_DEBUG @@ -279,101 +308,110 @@ QGLEngineShaderProg *QGLEngineSharedShaders::findProgramInCache(const QGLEngineS } } - QGLShader *vertexShader = 0; - QGLShader *fragShader = 0; - QGLEngineShaderProg *newProg = 0; - bool success = false; + QScopedPointer<QGLEngineShaderProg> newProg; do { - QByteArray source; + QByteArray fragSource; // Insert the custom stage before the srcPixel shader to work around an ATI driver bug // where you cannot forward declare a function that takes a sampler as argument. if (prog.srcPixelFragShader == CustomImageSrcFragmentShader) - source.append(prog.customStageSource); - source.append(qShaderSnippets[prog.mainFragShader]); - source.append(qShaderSnippets[prog.srcPixelFragShader]); + fragSource.append(prog.customStageSource); + fragSource.append(qShaderSnippets[prog.mainFragShader]); + fragSource.append(qShaderSnippets[prog.srcPixelFragShader]); if (prog.compositionFragShader) - source.append(qShaderSnippets[prog.compositionFragShader]); + fragSource.append(qShaderSnippets[prog.compositionFragShader]); if (prog.maskFragShader) - source.append(qShaderSnippets[prog.maskFragShader]); - fragShader = new QGLShader(QGLShader::Fragment, ctxGuard.context(), 0); - shaders.append(fragShader); - QByteArray description; + fragSource.append(qShaderSnippets[prog.maskFragShader]); + + QByteArray vertexSource; + vertexSource.append(qShaderSnippets[prog.mainVertexShader]); + vertexSource.append(qShaderSnippets[prog.positionVertexShader]); + + QScopedPointer<QGLShaderProgram> shaderProgram(new QGLShaderProgram(ctxGuard.context(), 0)); + + CachedShader shaderCache(fragSource, vertexSource); + bool inCache = shaderCache.load(shaderProgram.data(), ctxGuard.context()); + + if (!inCache) { + + QScopedPointer<QGLShader> fragShader(new QGLShader(QGLShader::Fragment, ctxGuard.context(), 0)); + QByteArray description; #if defined(QT_DEBUG) - // Name the shader for easier debugging - description.append("Fragment shader: main="); - description.append(snippetNameStr(prog.mainFragShader)); - description.append(", srcPixel="); - description.append(snippetNameStr(prog.srcPixelFragShader)); - if (prog.compositionFragShader) { - description.append(", composition="); - description.append(snippetNameStr(prog.compositionFragShader)); - } - if (prog.maskFragShader) { - description.append(", mask="); - description.append(snippetNameStr(prog.maskFragShader)); - } - fragShader->setObjectName(QString::fromLatin1(description)); + // Name the shader for easier debugging + description.append("Fragment shader: main="); + description.append(snippetNameStr(prog.mainFragShader)); + description.append(", srcPixel="); + description.append(snippetNameStr(prog.srcPixelFragShader)); + if (prog.compositionFragShader) { + description.append(", composition="); + description.append(snippetNameStr(prog.compositionFragShader)); + } + if (prog.maskFragShader) { + description.append(", mask="); + description.append(snippetNameStr(prog.maskFragShader)); + } + fragShader->setObjectName(QString::fromLatin1(description)); #endif - if (!fragShader->compileSourceCode(source)) { - qWarning() << "Warning:" << description << "failed to compile!"; - break; - } + if (!fragShader->compileSourceCode(fragSource)) { + qWarning() << "Warning:" << description << "failed to compile!"; + break; + } - source.clear(); - source.append(qShaderSnippets[prog.mainVertexShader]); - source.append(qShaderSnippets[prog.positionVertexShader]); - vertexShader = new QGLShader(QGLShader::Vertex, ctxGuard.context(), 0); - shaders.append(vertexShader); + QScopedPointer<QGLShader> vertexShader(new QGLShader(QGLShader::Vertex, ctxGuard.context(), 0)); #if defined(QT_DEBUG) - // Name the shader for easier debugging - description.clear(); - description.append("Vertex shader: main="); - description.append(snippetNameStr(prog.mainVertexShader)); - description.append(", position="); - description.append(snippetNameStr(prog.positionVertexShader)); - vertexShader->setObjectName(QString::fromLatin1(description)); + // Name the shader for easier debugging + description.clear(); + description.append("Vertex shader: main="); + description.append(snippetNameStr(prog.mainVertexShader)); + description.append(", position="); + description.append(snippetNameStr(prog.positionVertexShader)); + vertexShader->setObjectName(QString::fromLatin1(description)); #endif - if (!vertexShader->compileSourceCode(source)) { - qWarning() << "Warning:" << description << "failed to compile!"; - break; - } + if (!vertexShader->compileSourceCode(vertexSource)) { + qWarning() << "Warning:" << description << "failed to compile!"; + break; + } - newProg = new QGLEngineShaderProg(prog); - - // If the shader program's not found in the cache, create it now. - newProg->program = new QGLShaderProgram(ctxGuard.context(), 0); - newProg->program->addShader(vertexShader); - newProg->program->addShader(fragShader); - - // We have to bind the vertex attribute names before the program is linked: - newProg->program->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR); - if (newProg->useTextureCoords) - newProg->program->bindAttributeLocation("textureCoordArray", QT_TEXTURE_COORDS_ATTR); - if (newProg->useOpacityAttribute) - newProg->program->bindAttributeLocation("opacityArray", QT_OPACITY_ATTR); - if (newProg->usePmvMatrixAttribute) { - newProg->program->bindAttributeLocation("pmvMatrix1", QT_PMV_MATRIX_1_ATTR); - newProg->program->bindAttributeLocation("pmvMatrix2", QT_PMV_MATRIX_2_ATTR); - newProg->program->bindAttributeLocation("pmvMatrix3", QT_PMV_MATRIX_3_ATTR); + shaders.append(vertexShader.data()); + shaders.append(fragShader.data()); + shaderProgram->addShader(vertexShader.take()); + shaderProgram->addShader(fragShader.take()); + + // We have to bind the vertex attribute names before the program is linked: + shaderProgram->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR); + if (prog.useTextureCoords) + shaderProgram->bindAttributeLocation("textureCoordArray", QT_TEXTURE_COORDS_ATTR); + if (prog.useOpacityAttribute) + shaderProgram->bindAttributeLocation("opacityArray", QT_OPACITY_ATTR); + if (prog.usePmvMatrixAttribute) { + shaderProgram->bindAttributeLocation("pmvMatrix1", QT_PMV_MATRIX_1_ATTR); + shaderProgram->bindAttributeLocation("pmvMatrix2", QT_PMV_MATRIX_2_ATTR); + shaderProgram->bindAttributeLocation("pmvMatrix3", QT_PMV_MATRIX_3_ATTR); + } } + newProg.reset(new QGLEngineShaderProg(prog)); + newProg->program = shaderProgram.take(); + newProg->program->link(); - if (!newProg->program->isLinked()) { + if (newProg->program->isLinked()) { + if (!inCache) + shaderCache.store(newProg->program, ctxGuard.context()); + } else { QLatin1String none("none"); QLatin1String br("\n"); QString error; - error = QLatin1String("Shader program failed to link,") + error = QLatin1String("Shader program failed to link,"); #if defined(QT_DEBUG) - + br - + QLatin1String(" Shaders Used:") + br - + QLatin1String(" ") + vertexShader->objectName() + QLatin1String(": ") + br - + QLatin1String(vertexShader->sourceCode()) + br - + QLatin1String(" ") + fragShader->objectName() + QLatin1String(": ") + br - + QLatin1String(fragShader->sourceCode()) + br + error += QLatin1String("\n Shaders Used:\n"); + for (int i = 0; i < newProg->program->shaders().count(); ++i) { + QGLShader *shader = newProg->program->shaders().at(i); + error += QLatin1String(" ") + shader->objectName() + QLatin1String(": \n") + + QLatin1String(shader->sourceCode()) + br; + } #endif - + QLatin1String(" Error Log:\n") - + QLatin1String(" ") + newProg->program->log(); + error += QLatin1String(" Error Log:\n") + + QLatin1String(" ") + newProg->program->log(); qWarning() << error; break; } @@ -395,26 +433,10 @@ QGLEngineShaderProg *QGLEngineSharedShaders::findProgramInCache(const QGLEngineS } } - cachedPrograms.insert(0, newProg); - - success = true; + cachedPrograms.insert(0, newProg.data()); } while (false); - // Clean up everything if we weren't successful - if (!success) { - if (newProg) { - delete newProg; // Also deletes the QGLShaderProgram which in turn deletes the QGLShaders - newProg = 0; - } - else { - if (vertexShader) - delete vertexShader; - if (fragShader) - delete fragShader; - } - } - - return newProg; + return newProg.take(); } void QGLEngineSharedShaders::cleanupCustomStage(QGLCustomShaderStage* stage) diff --git a/src/opengl/gl2paintengineex/qglshadercache_meego_p.h b/src/opengl/gl2paintengineex/qglshadercache_meego_p.h new file mode 100644 index 0000000..5f51fc2 --- /dev/null +++ b/src/opengl/gl2paintengineex/qglshadercache_meego_p.h @@ -0,0 +1,457 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenGL module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QGLSHADERCACHE_MEEGO_P_H +#define QGLSHADERCACHE_MEEGO_P_H + +#include <QtCore/qglobal.h> + +#if defined(QT_MEEGO_EXPERIMENTAL_SHADERCACHE) && defined(QT_OPENGL_ES_2) + +#include <QtCore/qcryptographichash.h> +#include <QtCore/qsharedmemory.h> +#include <QtCore/qsystemsemaphore.h> + +#ifndef QT_BOOTSTRAPPED +# include <GLES2/gl2ext.h> +#endif +#if defined(QT_DEBUG) || defined(QT_MEEGO_EXPERIMENTAL_SHADERCACHE_TRACE) +# include <syslog.h> +#endif + +QT_BEGIN_HEADER + +/* + This cache stores internal Qt shader programs in shared memory. + + This header file is ugly on purpose and can only be included once. It is only to be used + for the internal shader cache, not as a generic cache for anyone's shaders. + + The cache stores either ShaderCacheMaxEntries shader programs or ShaderCacheDataSize kilobytes + of shader programs, whatever limit is reached first. + + The layout of the cache is as outlined in the CachedShaders struct. After some + integers, an array of headers is reserved, then comes the space for the actual binaries. + + Shader Programs are identified by the md5sum of their frag and vertex shader source code. + + Shader Programs are never removed. The cache never shrinks or re-shuffles. This is done + on purpose to ensure minimum amount of locking, no alignment problems and very few write + operations. + + Note: Locking the shader cache could be expensive, because the entire system might hang. + That's why the cache is immutable to minimize the time we need to keep it locked. + + Why is it Meego specific? + + First, the size is chosen so that it fits to generic meego usage. Second, on Meego, there's + always at least one Qt application active (the launcher), so the cache will never be destroyed. + Only when the last Qt app exits, the cache dies, which should only be when someone kills the + X11 server. And last but not least it was only tested with Meego's SGX driver. + + There's a small tool in src/opengl/util/meego that dumps the contents of the cache. + */ + +// anonymous namespace, prevent exporting of the private symbols +namespace +{ + +struct CachedShaderHeader +{ + /* the index in the data[] member of CachedShaders */ + int index; + /* the size of the binary shader */ + GLsizei size; + /* the format of the binary shader */ + GLenum format; + /* the md5sum of the frag+vertex shaders */ + char md5Sum[16]; +}; + +enum +{ + /* The maximum amount of shader programs the cache can hold */ + ShaderCacheMaxEntries = 20 +}; + +typedef CachedShaderHeader CachedShaderHeaders[ShaderCacheMaxEntries]; + +enum +{ + // ShaderCacheDataSize is 20k minus the other data members of CachedShaders + ShaderCacheDataSize = 1024 * ShaderCacheMaxEntries - sizeof(CachedShaderHeaders) - 2 * sizeof(int) +}; + +struct CachedShaders +{ + /* How much space is still available in the cache */ + inline int availableSize() const { return ShaderCacheDataSize - dataSize; } + + /* The current amount of cached shaders */ + int shaderCount; + + /* The current amount (in bytes) of cached data */ + int dataSize; + + /* The headers describing the shaders */ + CachedShaderHeaders headers; + + /* The actual binary data of the shader programs */ + char data[ShaderCacheDataSize]; +}; + +//#define QT_DEBUG_SHADER_CACHE +#ifdef QT_DEBUG_SHADER_CACHE +static QDebug shaderCacheDebug() +{ + return QDebug(QtDebugMsg); +} +#else +static inline QNoDebug shaderCacheDebug() { return QNoDebug(); } +#endif + +class ShaderCacheSharedMemory +{ +public: + ShaderCacheSharedMemory() + : shm(QLatin1String("qt_gles2_shadercache_" QT_VERSION_STR)) + { + // we need a system semaphore here, since cache creation and initialization must be atomic + QSystemSemaphore attachSemaphore(QLatin1String("qt_gles2_shadercache_mutex_" QT_VERSION_STR), 1); + + if (!attachSemaphore.acquire()) { + shaderCacheDebug() << "Unable to require shader cache semaphore:" << attachSemaphore.errorString(); + return; + } + + if (shm.attach()) { + // success! + shaderCacheDebug() << "Attached to shader cache"; + } else { + + // no cache exists - create and initialize it + if (shm.create(sizeof(CachedShaders))) { + shaderCacheDebug() << "Created new shader cache"; + initializeCache(); + } else { + shaderCacheDebug() << "Unable to create shader cache:" << shm.errorString(); + } + } + + attachSemaphore.release(); + } + + inline bool isAttached() const { return shm.isAttached(); } + + inline bool lock() { return shm.lock(); } + inline bool unlock() { return shm.unlock(); } + inline void *data() { return shm.data(); } + inline QString errorString() { return shm.errorString(); } + + ~ShaderCacheSharedMemory() + { + if (!shm.detach()) + shaderCacheDebug() << "Unable to detach shader cache" << shm.errorString(); + } + +private: + void initializeCache() + { + // no need to lock the shared memory since we're already protected by the + // attach system semaphore. + + void *data = shm.data(); + Q_ASSERT(data); + + memset(data, 0, sizeof(CachedShaders)); + } + + QSharedMemory shm; +}; + +class ShaderCacheLocker +{ +public: + inline ShaderCacheLocker(ShaderCacheSharedMemory *cache) + : shm(cache->lock() ? cache : (ShaderCacheSharedMemory *)0) + { + if (!shm) + shaderCacheDebug() << "Unable to lock shader cache" << cache->errorString(); + } + + inline bool isLocked() const { return shm; } + + inline ~ShaderCacheLocker() + { + if (!shm) + return; + if (!shm->unlock()) + shaderCacheDebug() << "Unable to unlock shader cache" << shm->errorString(); + } + +private: + ShaderCacheSharedMemory *shm; +}; + +#ifdef QT_BOOTSTRAPPED +} // end namespace +#else + +static void traceCacheOverflow(const char *message) +{ +#if defined(QT_DEBUG) || defined (QT_MEEGO_EXPERIMENTAL_SHADERCACHE_TRACE) + openlog(qPrintable(QCoreApplication::applicationName()), LOG_PID | LOG_ODELAY, LOG_USER); + syslog(LOG_DEBUG, message); + closelog(); +#endif + shaderCacheDebug() << message; +} + +Q_GLOBAL_STATIC(ShaderCacheSharedMemory, shaderCacheSharedMemory) + +/* + Finds the index of the shader program identified by md5Sum in the cache. + Note: Does NOT lock the cache for reading, the cache must already be locked! + + Returns -1 when no shader was found. + */ +static int qt_cache_index_unlocked(const QByteArray &md5Sum, CachedShaders *cache) +{ + for (int i = 0; i < cache->shaderCount; ++i) { + if (qstrncmp(md5Sum.constData(), cache->headers[i].md5Sum, 16) == 0) { + return i; + } + } + return -1; +} + +/* Returns the index of the shader identified by md5Sum */ +static int qt_cache_index(const QByteArray &md5Sum) +{ + ShaderCacheSharedMemory *shm = shaderCacheSharedMemory(); + if (!shm || !shm->isAttached()) + return false; + + Q_ASSERT(md5Sum.length() == 16); + + ShaderCacheLocker locker(shm); + if (!locker.isLocked()) + return false; + + void *data = shm->data(); + Q_ASSERT(data); + + CachedShaders *cache = reinterpret_cast<CachedShaders *>(data); + + return qt_cache_index_unlocked(md5Sum, cache); +} + +/* Loads the cached shader at index \a shaderIndex into \a program + * Note: Since the cache is immutable, this operation doesn't lock the shared memory. + */ +static bool qt_cached_shader(QGLShaderProgram *program, const QGLContext *ctx, int shaderIndex) +{ + Q_ASSERT(shaderIndex >= 0 && shaderIndex <= ShaderCacheMaxEntries); + Q_ASSERT(program); + + ShaderCacheSharedMemory *shm = shaderCacheSharedMemory(); + if (!shm || !shm->isAttached()) + return false; + + void *data = shm->data(); + Q_ASSERT(data); + + CachedShaders *cache = reinterpret_cast<CachedShaders *>(data); + + shaderCacheDebug() << "fetching cached shader at index" << shaderIndex + << "dataIndex" << cache->headers[shaderIndex].index + << "size" << cache->headers[shaderIndex].size + << "format" << cache->headers[shaderIndex].format; + + // call program->programId first, since that resolves the glProgramBinaryOES symbol + GLuint programId = program->programId(); + glProgramBinaryOES(programId, cache->headers[shaderIndex].format, + cache->data + cache->headers[shaderIndex].index, + cache->headers[shaderIndex].size); + + return true; +} + +/* Stores the shader program in the cache. Returns false if there's an error with the cache, or + if the cache is too small to hold the shader. */ +static bool qt_cache_shader(const QGLShaderProgram *shader, const QGLContext *ctx, const QByteArray &md5Sum) +{ + ShaderCacheSharedMemory *shm = shaderCacheSharedMemory(); + if (!shm || !shm->isAttached()) + return false; + + void *data = shm->data(); + Q_ASSERT(data); + + CachedShaders *cache = reinterpret_cast<CachedShaders *>(data); + + ShaderCacheLocker locker(shm); + if (!locker.isLocked()) + return false; + + int cacheIdx = cache->shaderCount; + if (cacheIdx >= ShaderCacheMaxEntries) { + traceCacheOverflow("Qt OpenGL shader cache index overflow!"); + return false; + } + + // now that we have the lock on the shared memory, make sure no one + // inserted the shader already while we were unlocked + if (qt_cache_index_unlocked(md5Sum, cache) != -1) + return true; // already cached + + shaderCacheDebug() << "Caching shader at index" << cacheIdx; + + GLint binaryLength = 0; + glGetProgramiv(shader->programId(), GL_PROGRAM_BINARY_LENGTH_OES, &binaryLength); + + if (!binaryLength) { + shaderCacheDebug() << "Unable to determine binary shader size!"; + return false; + } + + if (binaryLength > cache->availableSize()) { + traceCacheOverflow("Qt OpenGL shader cache data overflow!"); + return false; + } + + GLsizei size = 0; + GLenum format = 0; + glGetProgramBinaryOES(shader->programId(), binaryLength, &size, &format, + cache->data + cache->dataSize); + + if (!size) { + shaderCacheDebug() << "Unable to get binary shader!"; + return false; + } + + cache->headers[cacheIdx].index = cache->dataSize; + cache->dataSize += binaryLength; + ++cache->shaderCount; + cache->headers[cacheIdx].size = binaryLength; + cache->headers[cacheIdx].format = format; + + memcpy(cache->headers[cacheIdx].md5Sum, md5Sum.constData(), 16); + + shaderCacheDebug() << "cached shader size" << size + << "format" << format + << "binarySize" << binaryLength + << "cache index" << cacheIdx + << "data index" << cache->headers[cacheIdx].index; + + return true; +} + +} // namespace + +QT_BEGIN_NAMESPACE + +QT_MODULE(OpenGL) + +class CachedShader +{ +public: + CachedShader(const QByteArray &fragSource, const QByteArray &vertexSource) + : cacheIdx(-1) + { + QCryptographicHash md5Hash(QCryptographicHash::Md5); + + md5Hash.addData(fragSource); + md5Hash.addData(vertexSource); + + md5Sum = md5Hash.result(); + } + + bool isCached() + { + return cacheIndex() != -1; + } + + int cacheIndex() + { + if (cacheIdx != -1) + return cacheIdx; + cacheIdx = qt_cache_index(md5Sum); + return cacheIdx; + } + + bool load(QGLShaderProgram *program, const QGLContext *ctx) + { + if (cacheIndex() == -1) + return false; + return qt_cached_shader(program, ctx, cacheIdx); + } + + bool store(QGLShaderProgram *program, const QGLContext *ctx) + { + return qt_cache_shader(program, ctx, md5Sum); + } + +private: + QByteArray md5Sum; + int cacheIdx; +}; + + +QT_END_NAMESPACE + +#endif + +QT_END_HEADER + +#endif +#endif diff --git a/src/opengl/gl2paintengineex/qglshadercache_p.h b/src/opengl/gl2paintengineex/qglshadercache_p.h new file mode 100644 index 0000000..29616ae --- /dev/null +++ b/src/opengl/gl2paintengineex/qglshadercache_p.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenGL module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QGLSHADERCACHE_P_H +#define QGLSHADERCACHE_P_H + +#include <QtCore/qglobal.h> + +#if defined(QT_MEEGO_EXPERIMENTAL_SHADERCACHE) && defined(QT_OPENGL_ES_2) +# include "qglshadercache_meego_p.h" +#else + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(OpenGL) + +class QGLShaderProgram; +class QGLContext; + +class CachedShader +{ +public: + inline CachedShader(const QByteArray &, const QByteArray &) + {} + + inline bool isCached() + { + return false; + } + + inline bool load(QGLShaderProgram *, const QGLContext *) + { + return false; + } + + inline bool store(QGLShaderProgram *, const QGLContext *) + { + return false; + } +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif +#endif diff --git a/src/opengl/opengl.pro b/src/opengl/opengl.pro index 682e620..a089d55 100644 --- a/src/opengl/opengl.pro +++ b/src/opengl/opengl.pro @@ -60,7 +60,9 @@ SOURCES += qgl.cpp \ gl2paintengineex/qglcustomshaderstage_p.h \ gl2paintengineex/qtriangulatingstroker_p.h \ gl2paintengineex/qtriangulator_p.h \ - gl2paintengineex/qtextureglyphcache_gl_p.h + gl2paintengineex/qtextureglyphcache_gl_p.h \ + gl2paintengineex/qglshadercache_p.h \ + gl2paintengineex/qglshadercache_meego_p.h SOURCES += qglshaderprogram.cpp \ qglpixmapfilter.cpp \ diff --git a/src/opengl/util/meego/main.cpp b/src/opengl/util/meego/main.cpp new file mode 100644 index 0000000..0c9a915 --- /dev/null +++ b/src/opengl/util/meego/main.cpp @@ -0,0 +1,48 @@ +#include <QtCore/qdebug.h> + +#define QT_DEBUG_SHADER_CACHE +#define QT_MEEGO_EXPERIMENTAL_SHADERCACHE +#define QT_OPENGL_ES_2 +#define QT_BOOTSTRAPPED + +typedef int GLsizei; +typedef unsigned int GLenum; + +#include "../../gl2paintengineex/qglshadercache_meego_p.h" + +#include <stdlib.h> +#include <stdio.h> + +int main() +{ + ShaderCacheSharedMemory shm; + + if (!shm.isAttached()) { + fprintf(stderr, "Unable to attach to shared memory\n"); + return EXIT_FAILURE; + } + + ShaderCacheLocker locker(&shm); + if (!locker.isLocked()) { + fprintf(stderr, "Unable to lock shared memory\n"); + return EXIT_FAILURE; + } + + void *data = shm.data(); + Q_ASSERT(data); + + CachedShaders *cache = reinterpret_cast<CachedShaders *>(data); + + for (int i = 0; i < cache->shaderCount; ++i) { + printf("Shader %d: %d bytes\n", i, cache->headers[i].size); + } + + printf("\nSummary:\n\n" + " Amount of cached shaders: %d\n" + " Bytes used: %d\n" + " Bytes available: %d\n", + cache->shaderCount, cache->dataSize, cache->availableSize()); + + return EXIT_SUCCESS; +} + diff --git a/src/opengl/util/meego/shader-cache-introspector.pro b/src/opengl/util/meego/shader-cache-introspector.pro new file mode 100644 index 0000000..520e9a5 --- /dev/null +++ b/src/opengl/util/meego/shader-cache-introspector.pro @@ -0,0 +1,7 @@ +TEMPLATE = app + +SOURCES += main.cpp + +TARGET = shader-cache-introspector + +QT = core |