From 90567274b900b22ab6b1c016ee66b7915aa994c8 Mon Sep 17 00:00:00 2001 From: Tom Cooksey Date: Fri, 30 Oct 2009 12:38:00 +0100 Subject: Implement a simple caching algorithm for shader programs. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the number of programs held in the cache exceeds a threshold, the least frequantly used programs get deleted. This also covers programs with custom snippets of code. As a conequence, when a QGLCustomShaderStage gets deleted, any programs using that code will (eventually) be freed. Reviewed-By: Samuel Rødal --- .../gl2paintengineex/qglengineshadermanager.cpp | 71 +++++++++++++--------- .../gl2paintengineex/qglengineshadermanager_p.h | 13 +++- 2 files changed, 53 insertions(+), 31 deletions(-) diff --git a/src/opengl/gl2paintengineex/qglengineshadermanager.cpp b/src/opengl/gl2paintengineex/qglengineshadermanager.cpp index be0e98a..34c448f 100644 --- a/src/opengl/gl2paintengineex/qglengineshadermanager.cpp +++ b/src/opengl/gl2paintengineex/qglengineshadermanager.cpp @@ -231,12 +231,14 @@ QByteArray QGLEngineSharedShaders::snippetNameStr(SnippetName name) QGLEngineShaderProg *QGLEngineSharedShaders::findProgramInCache(const QGLEngineShaderProg &prog) { for (int i = 0; i < cachedPrograms.size(); ++i) { - if (cachedPrograms[i] == prog) - return &cachedPrograms[i]; + QGLEngineShaderProg *cachedProg = cachedPrograms[i]; + if (*cachedProg == prog) { + // Move the program to the top of the list as a poor-man's cache algo + cachedPrograms.move(i, 0); + return cachedProg; + } } - cachedPrograms.append(prog); - QGLEngineShaderProg &cached = cachedPrograms.last(); QByteArray source; source.append(qShaderSnippets[prog.mainFragShader]); source.append(qShaderSnippets[prog.srcPixelFragShader]); @@ -280,20 +282,22 @@ QGLEngineShaderProg *QGLEngineSharedShaders::findProgramInCache(const QGLEngineS vertexShader->setObjectName(QString::fromLatin1(description)); #endif + QGLEngineShaderProg* newProg = new QGLEngineShaderProg(prog); + // If the shader program's not found in the cache, create it now. - cached.program = new QGLShaderProgram(ctxGuard.context(), this); - cached.program->addShader(vertexShader); - cached.program->addShader(fragShader); + newProg->program = new QGLShaderProgram(ctxGuard.context(), this); + newProg->program->addShader(vertexShader); + newProg->program->addShader(fragShader); // We have to bind the vertex attribute names before the program is linked: - cached.program->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR); - if (cached.useTextureCoords) - cached.program->bindAttributeLocation("textureCoordArray", QT_TEXTURE_COORDS_ATTR); - if (cached.useOpacityAttribute) - cached.program->bindAttributeLocation("opacityArray", QT_OPACITY_ATTR); - - cached.program->link(); - if (!cached.program->isLinked()) { + 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); + + newProg->program->link(); + if (!newProg->program->isLinked()) { QLatin1String none("none"); QLatin1String br("\n"); QString error; @@ -307,32 +311,42 @@ QGLEngineShaderProg *QGLEngineSharedShaders::findProgramInCache(const QGLEngineS + QLatin1String(fragShader->sourceCode()) + br #endif + QLatin1String(" Error Log:\n") - + QLatin1String(" ") + cached.program->log(); + + QLatin1String(" ") + newProg->program->log(); qWarning() << error; - delete cached.program; - cachedPrograms.removeLast(); - return 0; - } else { - // taking the address here is safe since - // cachePrograms isn't resized anywhere else - return &cached; + delete newProg; // Deletes the QGLShaderProgram in it's destructor + newProg = 0; } -} + else { + if (cachedPrograms.count() > 30) { + // The cache is full, so delete the last 5 programs in the list. + // These programs will be least used, as a program us bumped to + // the top of the list when it's used. + for (int i = 0; i < 5; ++i) { + delete cachedPrograms.last(); + cachedPrograms.removeLast(); + } + } + cachedPrograms.insert(0, newProg); + } + + return newProg; +} void QGLEngineSharedShaders::cleanupCustomStage(QGLCustomShaderStage* stage) { // Remove any shader programs which has this as the custom shader src: for (int i = 0; i < cachedPrograms.size(); ++i) { - if (cachedPrograms.at(i).customStageSource == stage->source()) { - delete cachedPrograms.at(i).program; - cachedPrograms.removeAt(i--); + QGLEngineShaderProg *cachedProg = cachedPrograms[i]; + if (cachedProg->customStageSource == stage->source()) { + delete cachedProg; + cachedPrograms.removeAt(i); + i--; } } } - QGLEngineShaderManager::QGLEngineShaderManager(QGLContext* context) : ctx(context), shaderProgNeedsChanging(true), @@ -487,7 +501,6 @@ bool QGLEngineShaderManager::useCorrectShaderProg() } QGLEngineShaderProg requiredProgram; - requiredProgram.program = 0; bool texCoords = false; diff --git a/src/opengl/gl2paintengineex/qglengineshadermanager_p.h b/src/opengl/gl2paintengineex/qglengineshadermanager_p.h index fd73b44..59e50d0 100644 --- a/src/opengl/gl2paintengineex/qglengineshadermanager_p.h +++ b/src/opengl/gl2paintengineex/qglengineshadermanager_p.h @@ -366,13 +366,22 @@ private: QGLSharedResourceGuard ctxGuard; QGLShaderProgram *blitShaderProg; QGLShaderProgram *simpleShaderProg; - QList cachedPrograms; + QList cachedPrograms; static const char* qShaderSnippets[TotalSnippetCount]; }; -struct QGLEngineShaderProg + +class QGLEngineShaderProg { +public: + QGLEngineShaderProg() : program(0) {} + + ~QGLEngineShaderProg() { + if (program) + delete program; + } + QGLEngineSharedShaders::SnippetName mainVertexShader; QGLEngineSharedShaders::SnippetName positionVertexShader; QGLEngineSharedShaders::SnippetName mainFragShader; -- cgit v0.12