Skip to content

Commit

Permalink
Share and enable shader disk cache in QRhi OpenGL backend
Browse files Browse the repository at this point in the history
The expectation for it is to function identically to what we get with
QOpenGLShaderProgram. (same environment variables, same logging
categories, etc.).

QOpenGLProgramBinaryCache is now shared between the QOpenGL convenience
classes (like QOpenGLShaderProgram) and QRhi. To achieve more modularity
and to prepare for QOpenGLShaderProgram and friends moving out of QtGui,
this class cannot depend on QOpenGLShader* anymore. This involves adding
some minor conversions between QRhi and QOpenGL enums for example.

Change-Id: I2f4664e074823ea536281aea8006a6db159a7381
Reviewed-by: Christian Strømme <[email protected]>
  • Loading branch information
alpqr committed Sep 30, 2019
1 parent 6f9a215 commit e1ed2c3
Show file tree
Hide file tree
Showing 5 changed files with 271 additions and 120 deletions.
80 changes: 63 additions & 17 deletions src/gui/opengl/qopenglprogrambinarycache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
#include <QStandardPaths>
#include <QDir>
#include <QSaveFile>
#include <QCoreApplication>
#include <QLoggingCategory>
#include <QCryptographicHash>

Expand All @@ -54,7 +55,7 @@

QT_BEGIN_NAMESPACE

Q_DECLARE_LOGGING_CATEGORY(DBG_SHADER_CACHE)
Q_LOGGING_CATEGORY(lcOpenGLProgramDiskCache, "qt.opengl.diskcache")

#ifndef GL_CONTEXT_LOST
#define GL_CONTEXT_LOST 0x0507
Expand All @@ -64,6 +65,10 @@ Q_DECLARE_LOGGING_CATEGORY(DBG_SHADER_CACHE)
#define GL_PROGRAM_BINARY_LENGTH 0x8741
#endif

#ifndef GL_NUM_PROGRAM_BINARY_FORMATS
#define GL_NUM_PROGRAM_BINARY_FORMATS 0x87FE
#endif

const quint32 BINSHADER_MAGIC = 0x5174;
const quint32 BINSHADER_VERSION = 0x3;
const quint32 BINSHADER_QTVERSION = QT_VERSION;
Expand Down Expand Up @@ -123,7 +128,7 @@ QOpenGLProgramBinaryCache::QOpenGLProgramBinaryCache()
m_cacheDir = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + subPath;
m_cacheWritable = qt_ensureWritableDir(m_cacheDir);
}
qCDebug(DBG_SHADER_CACHE, "Cache location '%s' writable = %d", qPrintable(m_cacheDir), m_cacheWritable);
qCDebug(lcOpenGLProgramDiskCache, "Cache location '%s' writable = %d", qPrintable(m_cacheDir), m_cacheWritable);
}

QString QOpenGLProgramBinaryCache::cacheFileName(const QByteArray &cacheKey) const
Expand Down Expand Up @@ -154,24 +159,24 @@ static inline QByteArray readStr(const uchar **p)
bool QOpenGLProgramBinaryCache::verifyHeader(const QByteArray &buf) const
{
if (buf.size() < BASE_HEADER_SIZE) {
qCDebug(DBG_SHADER_CACHE, "Cached size too small");
qCDebug(lcOpenGLProgramDiskCache, "Cached size too small");
return false;
}
const uchar *p = reinterpret_cast<const uchar *>(buf.constData());
if (readUInt(&p) != BINSHADER_MAGIC) {
qCDebug(DBG_SHADER_CACHE, "Magic does not match");
qCDebug(lcOpenGLProgramDiskCache, "Magic does not match");
return false;
}
if (readUInt(&p) != BINSHADER_VERSION) {
qCDebug(DBG_SHADER_CACHE, "Version does not match");
qCDebug(lcOpenGLProgramDiskCache, "Version does not match");
return false;
}
if (readUInt(&p) != BINSHADER_QTVERSION) {
qCDebug(DBG_SHADER_CACHE, "Qt version does not match");
qCDebug(lcOpenGLProgramDiskCache, "Qt version does not match");
return false;
}
if (readUInt(&p) != sizeof(quintptr)) {
qCDebug(DBG_SHADER_CACHE, "Architecture does not match");
qCDebug(lcOpenGLProgramDiskCache, "Architecture does not match");
return false;
}
return true;
Expand All @@ -196,21 +201,21 @@ bool QOpenGLProgramBinaryCache::setProgramBinary(uint programId, uint blobFormat

GLenum err = funcs->glGetError();
if (err != GL_NO_ERROR) {
qCDebug(DBG_SHADER_CACHE, "Program binary failed to load for program %u, size %d, "
qCDebug(lcOpenGLProgramDiskCache, "Program binary failed to load for program %u, size %d, "
"format 0x%x, err = 0x%x",
programId, blobSize, blobFormat, err);
return false;
}
GLint linkStatus = 0;
funcs->glGetProgramiv(programId, GL_LINK_STATUS, &linkStatus);
if (linkStatus != GL_TRUE) {
qCDebug(DBG_SHADER_CACHE, "Program binary failed to load for program %u, size %d, "
qCDebug(lcOpenGLProgramDiskCache, "Program binary failed to load for program %u, size %d, "
"format 0x%x, linkStatus = 0x%x, err = 0x%x",
programId, blobSize, blobFormat, linkStatus, err);
return false;
}

qCDebug(DBG_SHADER_CACHE, "Program binary set for program %u, size %d, format 0x%x, err = 0x%x",
qCDebug(lcOpenGLProgramDiskCache, "Program binary set for program %u, size %d, format 0x%x, err = 0x%x",
programId, blobSize, blobFormat, err);
return true;
}
Expand Down Expand Up @@ -318,19 +323,19 @@ bool QOpenGLProgramBinaryCache::load(const QByteArray &cacheKey, uint programId)
if (vendor != info.glvendor) {
// readStr returns non-null terminated strings just pointing to inside
// 'p' so must print these via the stream qCDebug and not constData().
qCDebug(DBG_SHADER_CACHE) << "GL_VENDOR does not match" << vendor << info.glvendor;
qCDebug(lcOpenGLProgramDiskCache) << "GL_VENDOR does not match" << vendor << info.glvendor;
undertaker.setActive();
return false;
}
QByteArray renderer = readStr(&p);
if (renderer != info.glrenderer) {
qCDebug(DBG_SHADER_CACHE) << "GL_RENDERER does not match" << renderer << info.glrenderer;
qCDebug(lcOpenGLProgramDiskCache) << "GL_RENDERER does not match" << renderer << info.glrenderer;
undertaker.setActive();
return false;
}
QByteArray version = readStr(&p);
if (version != info.glversion) {
qCDebug(DBG_SHADER_CACHE) << "GL_VERSION does not match" << version << info.glversion;
qCDebug(lcOpenGLProgramDiskCache) << "GL_VERSION does not match" << version << info.glversion;
undertaker.setActive();
return false;
}
Expand Down Expand Up @@ -383,7 +388,7 @@ void QOpenGLProgramBinaryCache::save(const QByteArray &cacheKey, uint programId)

const int totalSize = headerSize + paddingSize + blobSize;

qCDebug(DBG_SHADER_CACHE, "Program binary is %d bytes, err = 0x%x, total %d", blobSize, funcs->glGetError(), totalSize);
qCDebug(lcOpenGLProgramDiskCache, "Program binary is %d bytes, err = 0x%x, total %d", blobSize, funcs->glGetError(), totalSize);
if (!blobSize)
return;

Expand Down Expand Up @@ -417,7 +422,7 @@ void QOpenGLProgramBinaryCache::save(const QByteArray &cacheKey, uint programId)
#endif
funcs->glGetProgramBinary(programId, blobSize, &outSize, &blobFormat, p);
if (blobSize != outSize) {
qCDebug(DBG_SHADER_CACHE, "glGetProgramBinary returned size %d instead of %d", outSize, blobSize);
qCDebug(lcOpenGLProgramDiskCache, "glGetProgramBinary returned size %d instead of %d", outSize, blobSize);
return;
}

Expand All @@ -433,9 +438,9 @@ void QOpenGLProgramBinaryCache::save(const QByteArray &cacheKey, uint programId)
if (f.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
if (f.write(blob) < blob.length())
#endif
qCDebug(DBG_SHADER_CACHE, "Failed to write %s to shader cache", qPrintable(f.fileName()));
qCDebug(lcOpenGLProgramDiskCache, "Failed to write %s to shader cache", qPrintable(f.fileName()));
} else {
qCDebug(DBG_SHADER_CACHE, "Failed to create %s in shader cache", qPrintable(f.fileName()));
qCDebug(lcOpenGLProgramDiskCache, "Failed to create %s in shader cache", qPrintable(f.fileName()));
}
}

Expand All @@ -452,4 +457,45 @@ void QOpenGLProgramBinaryCache::initializeProgramBinaryOES(QOpenGLContext *conte
}
#endif

QOpenGLProgramBinarySupportCheck::QOpenGLProgramBinarySupportCheck(QOpenGLContext *context)
: QOpenGLSharedResource(context->shareGroup()),
m_supported(false)
{
if (QCoreApplication::testAttribute(Qt::AA_DisableShaderDiskCache)) {
qCDebug(lcOpenGLProgramDiskCache, "Shader cache disabled via app attribute");
return;
}
if (qEnvironmentVariableIntValue("QT_DISABLE_SHADER_DISK_CACHE")) {
qCDebug(lcOpenGLProgramDiskCache, "Shader cache disabled via env var");
return;
}

QOpenGLContext *ctx = QOpenGLContext::currentContext();
if (ctx) {
if (ctx->isOpenGLES()) {
qCDebug(lcOpenGLProgramDiskCache, "OpenGL ES v%d context", ctx->format().majorVersion());
if (ctx->format().majorVersion() >= 3) {
m_supported = true;
} else {
const bool hasExt = ctx->hasExtension("GL_OES_get_program_binary");
qCDebug(lcOpenGLProgramDiskCache, "GL_OES_get_program_binary support = %d", hasExt);
if (hasExt)
m_supported = true;
}
} else {
const bool hasExt = ctx->hasExtension("GL_ARB_get_program_binary");
qCDebug(lcOpenGLProgramDiskCache, "GL_ARB_get_program_binary support = %d", hasExt);
if (hasExt)
m_supported = true;
}
if (m_supported) {
GLint fmtCount = 0;
ctx->functions()->glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &fmtCount);
qCDebug(lcOpenGLProgramDiskCache, "Supported binary format count = %d", fmtCount);
m_supported = fmtCount > 0;
}
}
qCDebug(lcOpenGLProgramDiskCache, "Shader cache supported = %d", m_supported);
}

QT_END_NAMESPACE
43 changes: 39 additions & 4 deletions src/gui/opengl/qopenglprogrambinarycache_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,21 +52,26 @@
//

#include <QtGui/qtguiglobal.h>
#include <QtGui/qopenglshaderprogram.h>
#include <QtCore/qcache.h>
#include <QtCore/qmutex.h>
#include <QtGui/private/qopenglcontext_p.h>
#include <QtGui/private/qshader_p.h>

QT_BEGIN_NAMESPACE

// These classes are also used by the OpenGL backend of QRhi. They must
// therefore stay independent from QOpenGLShader(Program). Must rely only on
// QOpenGLContext/Functions.

class QOpenGLProgramBinaryCache
{
public:
struct ShaderDesc {
ShaderDesc() { }
ShaderDesc(QOpenGLShader::ShaderType type, const QByteArray &source = QByteArray())
: type(type), source(source)
ShaderDesc(QShader::Stage stage, const QByteArray &source = QByteArray())
: stage(stage), source(source)
{ }
QOpenGLShader::ShaderType type;
QShader::Stage stage;
QByteArray source;
};
struct ProgramDesc {
Expand Down Expand Up @@ -104,6 +109,36 @@ class QOpenGLProgramBinaryCache
QMutex m_mutex;
};

// While unlikely, one application can in theory use contexts with different versions
// or profiles. Therefore any version- or extension-specific checks must be done on a
// per-context basis, not just once per process. QOpenGLSharedResource enables this,
// although it's once-per-sharing-context-group, not per-context. Still, this should
// be good enough in practice.
class QOpenGLProgramBinarySupportCheck : public QOpenGLSharedResource
{
public:
QOpenGLProgramBinarySupportCheck(QOpenGLContext *context);
void invalidateResource() override { }
void freeResource(QOpenGLContext *) override { }

bool isSupported() const { return m_supported; }

private:
bool m_supported;
};

class QOpenGLProgramBinarySupportCheckWrapper
{
public:
QOpenGLProgramBinarySupportCheck *get(QOpenGLContext *context)
{
return m_resource.value<QOpenGLProgramBinarySupportCheck>(context);
}

private:
QOpenGLMultiGroupSharedResource m_resource;
};

QT_END_NAMESPACE

#endif
Loading

0 comments on commit e1ed2c3

Please sign in to comment.