diff options
author | Gunnar Sletta <gunnar.sletta@jollamobile.com> | 2014-02-11 21:37:31 +0000 |
---|---|---|
committer | Gunnar Sletta <gunnar.sletta@jollamobile.com> | 2014-02-13 08:44:55 +0100 |
commit | 119f2023566d9ecad98a96a70fb5f944c6251e71 (patch) | |
tree | 85ac071f231a24ba08b811de00c51fee67b5a2d2 | |
parent | 41c6700014614ba30982840cefef8df0178fa7ba (diff) |
Program binary support via (GL_OES_get_program_binary).
Change-Id: I7993318d52a4c7f30865d3ccbca7aa2efc9d010f
Reviewed-by: Michael Brasser <michael.brasser@live.com>
-rw-r--r-- | customcontext/context.cpp | 4 | ||||
-rw-r--r-- | customcontext/context.h | 5 | ||||
-rw-r--r-- | customcontext/customcontext.pro | 10 | ||||
-rw-r--r-- | customcontext/programbinary.cpp | 276 |
4 files changed, 293 insertions, 2 deletions
diff --git a/customcontext/context.cpp b/customcontext/context.cpp index 9762f5b..2e9d04f 100644 --- a/customcontext/context.cpp +++ b/customcontext/context.cpp @@ -171,6 +171,10 @@ Context::Context(QObject *parent) qDebug(" - multisampling: %s, samples=%d", m_useMultisampling ? "yes" : "no", m_sampleCount); qDebug(" - depth buffer: %s", m_depthBuffer ? "yes" : "no"); +#ifdef PROGRAM_BINARY + qDebug(" - program binary: yes"); +#endif + #ifdef CUSTOMCONTEXT_MATERIALPRELOAD qDebug(" - material preload: %s", m_materialPreloading ? "yes" : "no"); #endif diff --git a/customcontext/context.h b/customcontext/context.h index f975fd4..ac018e3 100644 --- a/customcontext/context.h +++ b/customcontext/context.h @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Jolla Ltd, author: <gunnar.sletta@jollamobile.com> ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Scenegraph Playground module of the Qt Toolkit. @@ -73,6 +74,10 @@ public: QSGTexture *createTexture(const QImage &image) const; QSGRenderer *createRenderer(); +#ifdef PROGRAM_BINARY + void compile(QSGMaterialShader *shader, QSGMaterial *material, const char *vertex, const char *fragment); +#endif + #ifdef CUSTOMCONTEXT_DITHER bool m_dither; OrderedDither2x2 *m_ditherProgram; diff --git a/customcontext/customcontext.pro b/customcontext/customcontext.pro index a60ac0b..ce9de29 100644 --- a/customcontext/customcontext.pro +++ b/customcontext/customcontext.pro @@ -158,10 +158,16 @@ swaplistenanimationdriver:{ } else { message("msaa .....................: no") } +} else { + programbinary: { + message("programbinary ............: yes") + DEFINES += PROGRAM_BINARY + SOURCES += programbinary.cpp + } else { + message("programbinary ............: no") + } } - - surfaceformat:{ message("surfaceformat ............: yes") DEFINES += CUSTOMCONTEXT_SURFACEFORMAT diff --git a/customcontext/programbinary.cpp b/customcontext/programbinary.cpp new file mode 100644 index 0000000..ac331fd --- /dev/null +++ b/customcontext/programbinary.cpp @@ -0,0 +1,276 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Jolla Ltd, author: <gunnar.sletta@jollamobile.com> +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Scenegraph Playground module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "context.h" + +#include <QtCore/QCryptographicHash> +#include <QtCore/QFile> +#include <QtCore/QSaveFile> +#include <QtCore/QDir> +#include <QtCore/QElapsedTimer> + +#include <QtQuick/qsgmaterial.h> + +#include <EGL/egl.h> + +/* + * The programbinary support makes use of the GL_OES_get_program_binary + * extension to cache programs binaries for use in the scenegraph. + * + * Program binaries are by default stored under "/tmp/$USER-qsg-bs-store". + * The user can override this location using an environment variable. + * Program binaries are written and read with user privileges, as + * is the directory in which they are stored. + * + * Filenames are generated based on a SHA1 from the source code. + * + * Issues: + * - If there are conflicting SHA1's there will be problems, but in + * real life, this will not happen. + * - If multiple users use the same cache locations there will be + * read/write issues. Don't do that, it isn't meant to be supported. + */ + +#define GL_PROGRAM_BINARY_LENGTH 0x8741 +extern "C" { + typedef void (* _glGetProgramBinary)(GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary); + typedef void (* _glProgramBinary)(GLuint program, GLenum binaryFormat, const void *binary, GLint length); +} +static _glGetProgramBinary glGetProgramBinary; +static _glProgramBinary glProgramBinary; + +namespace CustomContext { + +class QSGMaterialShader_Accessor : public QSGMaterialShader { +public: + + static QSGMaterialShader_Accessor *from(QSGMaterialShader *s) { + return (QSGMaterialShader_Accessor *) s; + } + const char *vertexCode() const { return vertexShader(); } + const char *fragmentCode() const { return fragmentShader(); } +}; + +struct ProgramBinary +{ + QByteArray key; + QByteArray blob; + uint format; + QByteArray vsh; + QByteArray fsh; +}; + +class ProgramBinaryStore +{ +public: + ProgramBinaryStore() + : m_hash(QCryptographicHash::Sha1) + { + m_location = QString::fromLocal8Bit(qgetenv("QSG_PROGRAM_BINARY_STORE")); + if (m_location.isEmpty()) { + QString base = QString::fromLocal8Bit(qgetenv("XDG_RUNTIME_DIR")); + if (base.isEmpty()) + base = QDir().tempPath() + QStringLiteral("/.") + QString::fromLocal8Bit(qgetenv("USER")) + QStringLiteral("-"); + else + base += QStringLiteral("/"); + m_location = base + "qsg-pb-store"; + } + QDir().mkpath(m_location); +#ifdef CUSTOMCONTEXT_DEBUG + qDebug() << "Customcontext: binary shaders stored in:" << m_location; +#endif + + glGetProgramBinary = (_glGetProgramBinary) eglGetProcAddress("glGetProgramBinaryOES"); + glProgramBinary = (_glProgramBinary) eglGetProcAddress("glProgramBinaryOES"); + + Q_ASSERT(glGetProgramBinary && glProgramBinary); + } + + static ProgramBinaryStore *self() { + if (!instance) + instance = new ProgramBinaryStore(); + return instance; + } + + QByteArray key(QSGMaterialShader *shader, const char *v, const char *f); + QString fileName(const QByteArray &key) { return m_location + QStringLiteral("/") + QString::fromLatin1(key); } + ProgramBinary *lookup(const QByteArray key); + void insert(ProgramBinary *shader); + void purge(const QByteArray &key); + + void compileAndInsert(QSGRenderContext *rc, const QByteArray &key, QSGMaterialShader *s, QSGMaterial *m, const char *v, const char *f); + +private: + QCryptographicHash m_hash; + QHash<QByteArray, ProgramBinary *> m_store; + QMutex m_mutex; + + QString m_location; + + static ProgramBinaryStore *instance; +}; + +ProgramBinaryStore *ProgramBinaryStore::instance = 0; + +QByteArray ProgramBinaryStore::key(QSGMaterialShader *shader, const char *v, const char *f) +{ + QSGMaterialShader_Accessor *s = QSGMaterialShader_Accessor::from(shader); + const char *vsh = v ? v : s->vertexCode(); + const char *fsh = f ? f : s->fragmentCode(); + return QCryptographicHash::hash(QByteArray(vsh) + QByteArray(fsh), QCryptographicHash::Sha1).toHex(); +} + +ProgramBinary *ProgramBinaryStore::lookup(const QByteArray key) +{ + QMutexLocker lock(&m_mutex); + + ProgramBinary *s = m_store.value(key); + if (s) + return s; + + QFile file(fileName(key)); + if (file.open(QFile::ReadOnly)) { + QDataStream stream(&file); + s = new ProgramBinary(); + s->key = key; + stream >> s->format; + stream >> s->blob; + m_store.insert(key, s); + return s; + } + + return 0; +} + +void ProgramBinaryStore::insert(ProgramBinary *shader) +{ + QMutexLocker lock(&m_mutex); + + m_store.insert(shader->key, shader); + + QSaveFile file(fileName(shader->key)); + if (file.open(QFile::WriteOnly)) { + QDataStream stream(&file); + stream << shader->format; + stream << shader->blob; + file.commit(); + } +} + +void ProgramBinaryStore::purge(const QByteArray &key) +{ + QMutexLocker lock(&m_mutex); + + delete m_store.take(key); + + QFile file(fileName(key)); + if (file.exists()) + file.remove(); +} + +void ProgramBinaryStore::compileAndInsert(QSGRenderContext *rc, const QByteArray &key, QSGMaterialShader *s, QSGMaterial *m, const char *v, const char *f) +{ + // Use the baseclass impl to do the actual compilation + rc->QSGRenderContext::compile(s, m, v, f); + QOpenGLShaderProgram *p = s->program(); + if (p->isLinked()) { + ProgramBinary *b = new ProgramBinary; + int length = 0; + int id = p->programId(); + QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions(); + funcs->glGetProgramiv(id, GL_PROGRAM_BINARY_LENGTH, &length); + b->blob.resize(length); + glGetProgramBinary(id, b->blob.size(), &length, &b->format, b->blob.data()); + b->key = key; + if (length != b->blob.size()) { +#ifdef CUSTOMCONTEXT_DEBUG + qDebug() << " - glGetProgramBinary did not return expected results..."; +#endif + delete b; + } else { + insert(b); + } + } +} + +void RenderContext::compile(QSGMaterialShader *shader, QSGMaterial *material, const char *vertex, const char *fragment) +{ + Q_ASSERT(QOpenGLContext::currentContext()->extensions().contains("GL_OES_get_program_binary")); + + // We cannot cache shaders which have custom compilation + if (material->flags() & QSGMaterial::CustomCompileStep) { + QSGRenderContext::compile(shader, material, vertex, fragment); + return; + } + + ProgramBinaryStore *store = ProgramBinaryStore::self(); + QByteArray key = store->key(shader, vertex, fragment); + ProgramBinary *binary = store->lookup(key); + + if (!binary) { +#ifdef CUSTOMCONTEXT_DEBUG + qDebug() << " - program binary not found, compiling and inserting" << key; +#endif + store->compileAndInsert(this, key, shader, material, vertex, fragment); + + } else { +#ifdef CUSTOMCONTEXT_DEBUG + qDebug() << " - found stored program binary" << key; +#endif + QOpenGLShaderProgram *p = shader->program(); + // The program might not have an id yet. addShader(0) will trick it into making one. + if (p->programId() == 0) + p->addShader(0); + // Upload precompiled binary + glProgramBinary(p->programId(), binary->format, binary->blob.data(), binary->blob.size()); + p->link(); + if (!p->isLinked()) { +#ifdef CUSTOMCONTEXT_DEBUG + qDebug() << " - program binary" << key << "failed to link, purging and recreating"; +#endif + // If it failed, purge the binary from the store and compile and insert a new one. + store->purge(key); + store->compileAndInsert(this, key, shader, material, vertex, fragment); + } + } +} + +} |