Share and enable shader disk cache in QRhi OpenGL backend
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 <christian.stromme@qt.io>
This commit is contained in:
parent
6f9a215cc4
commit
e1ed2c3864
@ -44,6 +44,7 @@
|
||||
#include <QStandardPaths>
|
||||
#include <QDir>
|
||||
#include <QSaveFile>
|
||||
#include <QCoreApplication>
|
||||
#include <QLoggingCategory>
|
||||
#include <QCryptographicHash>
|
||||
|
||||
@ -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
|
||||
@ -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;
|
||||
@ -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
|
||||
@ -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;
|
||||
@ -196,7 +201,7 @@ 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;
|
||||
@ -204,13 +209,13 @@ bool QOpenGLProgramBinaryCache::setProgramBinary(uint programId, uint blobFormat
|
||||
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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
@ -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 {
|
||||
@ -104,6 +109,36 @@ private:
|
||||
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
|
||||
|
@ -47,7 +47,6 @@
|
||||
#include <QtCore/qvarlengtharray.h>
|
||||
#include <QtCore/qvector.h>
|
||||
#include <QtCore/qloggingcategory.h>
|
||||
#include <QtCore/qcoreapplication.h>
|
||||
#include <QtGui/qtransform.h>
|
||||
#include <QtGui/QColor>
|
||||
#include <QtGui/QSurfaceFormat>
|
||||
@ -178,7 +177,7 @@ QT_BEGIN_NAMESPACE
|
||||
(requires OpenGL >= 4.3 or OpenGL ES >= 3.1).
|
||||
*/
|
||||
|
||||
Q_LOGGING_CATEGORY(DBG_SHADER_CACHE, "qt.opengl.diskcache")
|
||||
Q_DECLARE_LOGGING_CATEGORY(lcOpenGLProgramDiskCache)
|
||||
|
||||
// For GLES 3.1/3.2
|
||||
#ifndef GL_GEOMETRY_SHADER
|
||||
@ -209,10 +208,6 @@ Q_LOGGING_CATEGORY(DBG_SHADER_CACHE, "qt.opengl.diskcache")
|
||||
#define GL_PATCH_DEFAULT_INNER_LEVEL 0x8E73
|
||||
#endif
|
||||
|
||||
#ifndef GL_NUM_PROGRAM_BINARY_FORMATS
|
||||
#define GL_NUM_PROGRAM_BINARY_FORMATS 0x87FE
|
||||
#endif
|
||||
|
||||
#ifndef QT_OPENGL_ES_2
|
||||
static inline bool isFormatGLES(const QSurfaceFormat &f)
|
||||
{
|
||||
@ -1080,6 +1075,44 @@ bool QOpenGLShaderProgram::addCacheableShaderFromSourceCode(QOpenGLShader::Shade
|
||||
return addCacheableShaderFromSourceCode(type, QByteArray(source));
|
||||
}
|
||||
|
||||
static inline QShader::Stage qt_shaderTypeToStage(QOpenGLShader::ShaderType type)
|
||||
{
|
||||
switch (type) {
|
||||
case QOpenGLShader::Vertex:
|
||||
return QShader::VertexStage;
|
||||
case QOpenGLShader::Fragment:
|
||||
return QShader::FragmentStage;
|
||||
case QOpenGLShader::Geometry:
|
||||
return QShader::GeometryStage;
|
||||
case QOpenGLShader::TessellationControl:
|
||||
return QShader::TessellationControlStage;
|
||||
case QOpenGLShader::TessellationEvaluation:
|
||||
return QShader::TessellationEvaluationStage;
|
||||
case QOpenGLShader::Compute:
|
||||
return QShader::ComputeStage;
|
||||
}
|
||||
return QShader::VertexStage;
|
||||
}
|
||||
|
||||
static inline QOpenGLShader::ShaderType qt_shaderStageToType(QShader::Stage stage)
|
||||
{
|
||||
switch (stage) {
|
||||
case QShader::VertexStage:
|
||||
return QOpenGLShader::Vertex;
|
||||
case QShader::TessellationControlStage:
|
||||
return QOpenGLShader::TessellationControl;
|
||||
case QShader::TessellationEvaluationStage:
|
||||
return QOpenGLShader::TessellationEvaluation;
|
||||
case QShader::GeometryStage:
|
||||
return QOpenGLShader::Geometry;
|
||||
case QShader::FragmentStage:
|
||||
return QOpenGLShader::Fragment;
|
||||
case QShader::ComputeStage:
|
||||
return QOpenGLShader::Compute;
|
||||
}
|
||||
return QOpenGLShader::Vertex;
|
||||
}
|
||||
|
||||
/*!
|
||||
\overload
|
||||
|
||||
@ -1108,7 +1141,7 @@ bool QOpenGLShaderProgram::addCacheableShaderFromSourceCode(QOpenGLShader::Shade
|
||||
if (d->isCacheDisabled())
|
||||
return addShaderFromSourceCode(type, source);
|
||||
|
||||
d->binaryProgram.shaders.append(QOpenGLProgramBinaryCache::ShaderDesc(type, source));
|
||||
d->binaryProgram.shaders.append(QOpenGLProgramBinaryCache::ShaderDesc(qt_shaderTypeToStage(type), source));
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1165,7 +1198,7 @@ bool QOpenGLShaderProgram::addCacheableShaderFromSourceFile(QOpenGLShader::Shade
|
||||
if (d->isCacheDisabled())
|
||||
return addShaderFromSourceFile(type, fileName);
|
||||
|
||||
QOpenGLProgramBinaryCache::ShaderDesc shader(type);
|
||||
QOpenGLProgramBinaryCache::ShaderDesc shader(qt_shaderTypeToStage(type));
|
||||
// NB! It could be tempting to defer reading the file contents and just
|
||||
// hash the filename as the cache key, perhaps combined with last-modified
|
||||
// timestamp checks. However, this would raise a number of issues (no
|
||||
@ -3719,77 +3752,6 @@ bool QOpenGLShader::hasOpenGLShaders(ShaderType type, QOpenGLContext *context)
|
||||
return true;
|
||||
}
|
||||
|
||||
// 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;
|
||||
};
|
||||
|
||||
QOpenGLProgramBinarySupportCheck::QOpenGLProgramBinarySupportCheck(QOpenGLContext *context)
|
||||
: QOpenGLSharedResource(context->shareGroup()),
|
||||
m_supported(false)
|
||||
{
|
||||
if (QCoreApplication::testAttribute(Qt::AA_DisableShaderDiskCache)) {
|
||||
qCDebug(DBG_SHADER_CACHE, "Shader cache disabled via app attribute");
|
||||
return;
|
||||
}
|
||||
if (qEnvironmentVariableIntValue("QT_DISABLE_SHADER_DISK_CACHE")) {
|
||||
qCDebug(DBG_SHADER_CACHE, "Shader cache disabled via env var");
|
||||
return;
|
||||
}
|
||||
|
||||
QOpenGLContext *ctx = QOpenGLContext::currentContext();
|
||||
if (ctx) {
|
||||
if (ctx->isOpenGLES()) {
|
||||
qCDebug(DBG_SHADER_CACHE, "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(DBG_SHADER_CACHE, "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(DBG_SHADER_CACHE, "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(DBG_SHADER_CACHE, "Supported binary format count = %d", fmtCount);
|
||||
m_supported = fmtCount > 0;
|
||||
}
|
||||
}
|
||||
qCDebug(DBG_SHADER_CACHE, "Shader cache supported = %d", m_supported);
|
||||
}
|
||||
|
||||
class QOpenGLProgramBinarySupportCheckWrapper
|
||||
{
|
||||
public:
|
||||
QOpenGLProgramBinarySupportCheck *get(QOpenGLContext *context)
|
||||
{
|
||||
return m_resource.value<QOpenGLProgramBinarySupportCheck>(context);
|
||||
}
|
||||
|
||||
private:
|
||||
QOpenGLMultiGroupSharedResource m_resource;
|
||||
};
|
||||
|
||||
bool QOpenGLShaderProgramPrivate::isCacheDisabled() const
|
||||
{
|
||||
static QOpenGLProgramBinarySupportCheckWrapper binSupportCheck;
|
||||
@ -3800,7 +3762,7 @@ bool QOpenGLShaderProgramPrivate::compileCacheable()
|
||||
{
|
||||
Q_Q(QOpenGLShaderProgram);
|
||||
for (const QOpenGLProgramBinaryCache::ShaderDesc &shader : qAsConst(binaryProgram.shaders)) {
|
||||
QScopedPointer<QOpenGLShader> s(new QOpenGLShader(shader.type, q));
|
||||
QScopedPointer<QOpenGLShader> s(new QOpenGLShader(qt_shaderStageToType(shader.stage), q));
|
||||
if (!s->compileSourceCode(shader.source)) {
|
||||
log = s->log();
|
||||
return false;
|
||||
@ -3819,19 +3781,19 @@ bool QOpenGLShaderProgramPrivate::linkBinary()
|
||||
Q_Q(QOpenGLShaderProgram);
|
||||
|
||||
const QByteArray cacheKey = binaryProgram.cacheKey();
|
||||
if (DBG_SHADER_CACHE().isEnabled(QtDebugMsg))
|
||||
qCDebug(DBG_SHADER_CACHE, "program with %d shaders, cache key %s",
|
||||
if (lcOpenGLProgramDiskCache().isEnabled(QtDebugMsg))
|
||||
qCDebug(lcOpenGLProgramDiskCache, "program with %d shaders, cache key %s",
|
||||
binaryProgram.shaders.count(), cacheKey.constData());
|
||||
|
||||
bool needsCompile = true;
|
||||
if (binCache.load(cacheKey, q->programId())) {
|
||||
qCDebug(DBG_SHADER_CACHE, "Program binary received from cache");
|
||||
qCDebug(lcOpenGLProgramDiskCache, "Program binary received from cache");
|
||||
needsCompile = false;
|
||||
}
|
||||
|
||||
bool needsSave = false;
|
||||
if (needsCompile) {
|
||||
qCDebug(DBG_SHADER_CACHE, "Program binary not in cache, compiling");
|
||||
qCDebug(lcOpenGLProgramDiskCache, "Program binary not in cache, compiling");
|
||||
if (compileCacheable())
|
||||
needsSave = true;
|
||||
else
|
||||
|
@ -39,6 +39,7 @@
|
||||
#include <QOffscreenSurface>
|
||||
#include <QOpenGLContext>
|
||||
#include <QtGui/private/qopenglextensions_p.h>
|
||||
#include <QtGui/private/qopenglprogrambinarycache_p.h>
|
||||
#include <qmath.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
@ -275,6 +276,8 @@ QT_BEGIN_NAMESPACE
|
||||
#define GL_POINT_SPRITE 0x8861
|
||||
#endif
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(lcOpenGLProgramDiskCache)
|
||||
|
||||
/*!
|
||||
Constructs a new QRhiGles2InitParams.
|
||||
|
||||
@ -2709,8 +2712,7 @@ static inline GLenum toGlShaderType(QRhiShaderStage::Type type)
|
||||
}
|
||||
}
|
||||
|
||||
bool QRhiGles2::compileShader(GLuint program, const QRhiShaderStage &shaderStage,
|
||||
QShaderDescription *desc, int *glslVersionUsed)
|
||||
QByteArray QRhiGles2::shaderSource(const QRhiShaderStage &shaderStage, int *glslVersion)
|
||||
{
|
||||
const QShader bakedShader = shaderStage.shader();
|
||||
QVector<int> versionsToTry;
|
||||
@ -2729,8 +2731,8 @@ bool QRhiGles2::compileShader(GLuint program, const QRhiShaderStage &shaderStage
|
||||
QShaderVersion ver(v, QShaderVersion::GlslEs);
|
||||
source = bakedShader.shader({ QShader::GlslShader, ver, shaderStage.shaderVariant() }).shader();
|
||||
if (!source.isEmpty()) {
|
||||
if (glslVersionUsed)
|
||||
*glslVersionUsed = v;
|
||||
if (glslVersion)
|
||||
*glslVersion = v;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -2759,8 +2761,8 @@ bool QRhiGles2::compileShader(GLuint program, const QRhiShaderStage &shaderStage
|
||||
for (int v : versionsToTry) {
|
||||
source = bakedShader.shader({ QShader::GlslShader, v, shaderStage.shaderVariant() }).shader();
|
||||
if (!source.isEmpty()) {
|
||||
if (glslVersionUsed)
|
||||
*glslVersionUsed = v;
|
||||
if (glslVersion)
|
||||
*glslVersion = v;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -2768,8 +2770,15 @@ bool QRhiGles2::compileShader(GLuint program, const QRhiShaderStage &shaderStage
|
||||
if (source.isEmpty()) {
|
||||
qWarning() << "No GLSL shader code found (versions tried: " << versionsToTry
|
||||
<< ") in baked shader" << bakedShader;
|
||||
return false;
|
||||
}
|
||||
return source;
|
||||
}
|
||||
|
||||
bool QRhiGles2::compileShader(GLuint program, const QRhiShaderStage &shaderStage, int *glslVersion)
|
||||
{
|
||||
const QByteArray source = shaderSource(shaderStage, glslVersion);
|
||||
if (source.isEmpty())
|
||||
return false;
|
||||
|
||||
GLuint shader;
|
||||
auto cacheIt = m_shaderCache.constFind(shaderStage);
|
||||
@ -2806,7 +2815,6 @@ bool QRhiGles2::compileShader(GLuint program, const QRhiShaderStage &shaderStage
|
||||
|
||||
f->glAttachShader(program, shader);
|
||||
|
||||
*desc = bakedShader.description();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -2861,6 +2869,68 @@ void QRhiGles2::gatherSamplers(GLuint program, const QShaderDescription::InOutVa
|
||||
}
|
||||
}
|
||||
|
||||
bool QRhiGles2::isProgramBinaryDiskCacheEnabled() const
|
||||
{
|
||||
static QOpenGLProgramBinarySupportCheckWrapper checker;
|
||||
return checker.get(ctx)->isSupported();
|
||||
}
|
||||
|
||||
static QOpenGLProgramBinaryCache qrhi_programBinaryCache;
|
||||
|
||||
static inline QShader::Stage toShaderStage(QRhiShaderStage::Type type)
|
||||
{
|
||||
switch (type) {
|
||||
case QRhiShaderStage::Vertex:
|
||||
return QShader::VertexStage;
|
||||
case QRhiShaderStage::Fragment:
|
||||
return QShader::FragmentStage;
|
||||
case QRhiShaderStage::Compute:
|
||||
return QShader::ComputeStage;
|
||||
default:
|
||||
Q_UNREACHABLE();
|
||||
return QShader::VertexStage;
|
||||
}
|
||||
}
|
||||
|
||||
QRhiGles2::DiskCacheResult QRhiGles2::tryLoadFromDiskCache(const QRhiShaderStage *stages, int stageCount,
|
||||
GLuint program, QByteArray *cacheKey)
|
||||
{
|
||||
QRhiGles2::DiskCacheResult result = QRhiGles2::DiskCacheMiss;
|
||||
QByteArray diskCacheKey;
|
||||
|
||||
if (isProgramBinaryDiskCacheEnabled()) {
|
||||
QOpenGLProgramBinaryCache::ProgramDesc binaryProgram;
|
||||
for (int i = 0; i < stageCount; ++i) {
|
||||
const QRhiShaderStage &stage(stages[i]);
|
||||
const QByteArray source = shaderSource(stage, nullptr);
|
||||
if (source.isEmpty())
|
||||
return QRhiGles2::DiskCacheError;
|
||||
binaryProgram.shaders.append(QOpenGLProgramBinaryCache::ShaderDesc(toShaderStage(stage.type()), source));
|
||||
}
|
||||
|
||||
diskCacheKey = binaryProgram.cacheKey();
|
||||
if (qrhi_programBinaryCache.load(diskCacheKey, program)) {
|
||||
qCDebug(lcOpenGLProgramDiskCache, "Program binary received from cache, program %u, key %s",
|
||||
program, diskCacheKey.constData());
|
||||
result = QRhiGles2::DiskCacheHit;
|
||||
}
|
||||
}
|
||||
|
||||
if (cacheKey)
|
||||
*cacheKey = diskCacheKey;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void QRhiGles2::trySaveToDiskCache(GLuint program, const QByteArray &cacheKey)
|
||||
{
|
||||
if (isProgramBinaryDiskCacheEnabled()) {
|
||||
qCDebug(lcOpenGLProgramDiskCache, "Saving program binary, program %u, key %s",
|
||||
program, cacheKey.constData());
|
||||
qrhi_programBinaryCache.save(cacheKey, program);
|
||||
}
|
||||
}
|
||||
|
||||
QGles2Buffer::QGles2Buffer(QRhiImplementation *rhi, Type type, UsageFlags usage, int size)
|
||||
: QRhiBuffer(rhi, type, usage, size)
|
||||
{
|
||||
@ -3547,17 +3617,29 @@ bool QGles2GraphicsPipeline::build()
|
||||
|
||||
program = rhiD->f->glCreateProgram();
|
||||
|
||||
QByteArray diskCacheKey;
|
||||
QRhiGles2::DiskCacheResult diskCacheResult = rhiD->tryLoadFromDiskCache(m_shaderStages.constData(),
|
||||
m_shaderStages.count(),
|
||||
program,
|
||||
&diskCacheKey);
|
||||
if (diskCacheResult == QRhiGles2::DiskCacheError)
|
||||
return false;
|
||||
|
||||
const bool needsCompile = diskCacheResult == QRhiGles2::DiskCacheMiss;
|
||||
|
||||
QShaderDescription vsDesc;
|
||||
QShaderDescription fsDesc;
|
||||
for (const QRhiShaderStage &shaderStage : qAsConst(m_shaderStages)) {
|
||||
const bool isVertex = shaderStage.type() == QRhiShaderStage::Vertex;
|
||||
const bool isFragment = shaderStage.type() == QRhiShaderStage::Fragment;
|
||||
if (isVertex) {
|
||||
if (!rhiD->compileShader(program, shaderStage, &vsDesc, nullptr))
|
||||
if (needsCompile && !rhiD->compileShader(program, shaderStage, nullptr))
|
||||
return false;
|
||||
vsDesc = shaderStage.shader().description();
|
||||
} else if (isFragment) {
|
||||
if (!rhiD->compileShader(program, shaderStage, &fsDesc, nullptr))
|
||||
if (needsCompile && !rhiD->compileShader(program, shaderStage, nullptr))
|
||||
return false;
|
||||
fsDesc = shaderStage.shader().description();
|
||||
}
|
||||
}
|
||||
|
||||
@ -3566,9 +3648,12 @@ bool QGles2GraphicsPipeline::build()
|
||||
rhiD->f->glBindAttribLocation(program, GLuint(inVar.location), name.constData());
|
||||
}
|
||||
|
||||
if (!rhiD->linkProgram(program))
|
||||
if (needsCompile && !rhiD->linkProgram(program))
|
||||
return false;
|
||||
|
||||
if (needsCompile)
|
||||
rhiD->trySaveToDiskCache(program, diskCacheKey);
|
||||
|
||||
for (const QShaderDescription::UniformBlock &ub : vsDesc.uniformBlocks())
|
||||
rhiD->gatherUniforms(program, ub, &uniforms);
|
||||
|
||||
@ -3629,11 +3714,24 @@ bool QGles2ComputePipeline::build()
|
||||
program = rhiD->f->glCreateProgram();
|
||||
QShaderDescription csDesc;
|
||||
|
||||
if (!rhiD->compileShader(program, m_shaderStage, &csDesc, nullptr))
|
||||
QByteArray diskCacheKey;
|
||||
QRhiGles2::DiskCacheResult diskCacheResult = rhiD->tryLoadFromDiskCache(&m_shaderStage, 1, program, &diskCacheKey);
|
||||
if (diskCacheResult == QRhiGles2::DiskCacheError)
|
||||
return false;
|
||||
if (!rhiD->linkProgram(program))
|
||||
|
||||
const bool needsCompile = diskCacheResult == QRhiGles2::DiskCacheMiss;
|
||||
|
||||
if (needsCompile && !rhiD->compileShader(program, m_shaderStage, nullptr))
|
||||
return false;
|
||||
|
||||
csDesc = m_shaderStage.shader().description();
|
||||
|
||||
if (needsCompile && !rhiD->linkProgram(program))
|
||||
return false;
|
||||
|
||||
if (needsCompile)
|
||||
rhiD->trySaveToDiskCache(program, diskCacheKey);
|
||||
|
||||
for (const QShaderDescription::UniformBlock &ub : csDesc.uniformBlocks())
|
||||
rhiD->gatherUniforms(program, ub, &uniforms);
|
||||
for (const QShaderDescription::InOutVariable &v : csDesc.combinedImageSamplers())
|
||||
|
@ -692,13 +692,23 @@ public:
|
||||
bool *wantsColorClear = nullptr, bool *wantsDsClear = nullptr);
|
||||
void enqueueBarriersForPass(QGles2CommandBuffer *cbD);
|
||||
int effectiveSampleCount(int sampleCount) const;
|
||||
bool compileShader(GLuint program, const QRhiShaderStage &shaderStage,
|
||||
QShaderDescription *desc, int *glslVersionUsed);
|
||||
QByteArray shaderSource(const QRhiShaderStage &shaderStage, int *glslVersion);
|
||||
bool compileShader(GLuint program, const QRhiShaderStage &shaderStage, int *glslVersion);
|
||||
bool linkProgram(GLuint program);
|
||||
void gatherUniforms(GLuint program, const QShaderDescription::UniformBlock &ub,
|
||||
QVector<QGles2UniformDescription> *dst);
|
||||
void gatherSamplers(GLuint program, const QShaderDescription::InOutVariable &v,
|
||||
QVector<QGles2SamplerDescription> *dst);
|
||||
bool isProgramBinaryDiskCacheEnabled() const;
|
||||
|
||||
enum DiskCacheResult {
|
||||
DiskCacheHit,
|
||||
DiskCacheMiss,
|
||||
DiskCacheError
|
||||
};
|
||||
DiskCacheResult tryLoadFromDiskCache(const QRhiShaderStage *stages, int stageCount,
|
||||
GLuint program, QByteArray *cacheKey);
|
||||
void trySaveToDiskCache(GLuint program, const QByteArray &cacheKey);
|
||||
|
||||
QOpenGLContext *ctx = nullptr;
|
||||
bool importedContext = false;
|
||||
|
Loading…
Reference in New Issue
Block a user