summaryrefslogtreecommitdiffstats
path: root/src/gui/rhi
diff options
context:
space:
mode:
authorLaszlo Agocs <laszlo.agocs@qt.io>2019-09-03 11:59:24 +0200
committerLaszlo Agocs <laszlo.agocs@qt.io>2019-09-09 20:42:38 +0200
commit7af6649e884f4ba9fa3fde0333090b93f62a13c0 (patch)
tree50c8123a114b6f29a949c910751ce999e9bc7c81 /src/gui/rhi
parent89d0a03c067b42155b1a2d310f8514f595abfd61 (diff)
rhi: gl, metal, d3d: Reuse shader objects when source is the same
Now that Qt Quick's batch renderer misses one level of shader source caching due to the nature of pipeline state objects, it can be useful to keep and reuse shader objects when the hash of the source code matches. The goal here is to allow Qt Quick to be on par with what the direct OpenGL path has when it comes to caching shader sources and compilation results. The program binary disk cache is not in scope in this patch. Also adds QRhi::releaseCachedResources(), similarly to what the scenegraph has. This can be called to clear caches such as the shader object cache we keep here. Change-Id: Ie3d81d823f61fa65ec814439e882c498f7774d43 Reviewed-by: Christian Strømme <christian.stromme@qt.io>
Diffstat (limited to 'src/gui/rhi')
-rw-r--r--src/gui/rhi/qrhi.cpp17
-rw-r--r--src/gui/rhi/qrhi_p.h2
-rw-r--r--src/gui/rhi/qrhi_p_p.h3
-rw-r--r--src/gui/rhi/qrhid3d11.cpp116
-rw-r--r--src/gui/rhi/qrhid3d11_p_p.h10
-rw-r--r--src/gui/rhi/qrhigles2.cpp63
-rw-r--r--src/gui/rhi/qrhigles2_p_p.h3
-rw-r--r--src/gui/rhi/qrhimetal.mm229
-rw-r--r--src/gui/rhi/qrhimetal_p_p.h1
-rw-r--r--src/gui/rhi/qrhinull.cpp5
-rw-r--r--src/gui/rhi/qrhinull_p_p.h1
-rw-r--r--src/gui/rhi/qrhivulkan.cpp5
-rw-r--r--src/gui/rhi/qrhivulkan_p_p.h1
13 files changed, 315 insertions, 141 deletions
diff --git a/src/gui/rhi/qrhi.cpp b/src/gui/rhi/qrhi.cpp
index 4414b61d55..88d2f73541 100644
--- a/src/gui/rhi/qrhi.cpp
+++ b/src/gui/rhi/qrhi.cpp
@@ -5020,6 +5020,23 @@ QRhiProfiler *QRhi::profiler()
}
/*!
+ Attempts to release resources in the backend's caches. This can include both
+ CPU and GPU resources. Only memory and resources that can be recreated
+ automatically are in scope. As an example, if the backend's
+ QRhiGraphicsPipeline implementation maintains a cache of shader compilation
+ results, calling this function leads to emptying that cache, thus
+ potentially freeing up memory and graphics resources.
+
+ Calling this function makes sense in resource constrained environments,
+ where at a certain point there is a need to ensure minimal resource usage,
+ at the expense of performance.
+ */
+void QRhi::releaseCachedResources()
+{
+ d->releaseCachedResources();
+}
+
+/*!
\return a new graphics pipeline resource.
\sa QRhiResource::release()
diff --git a/src/gui/rhi/qrhi_p.h b/src/gui/rhi/qrhi_p.h
index 2d36c19e99..928d1f8fa7 100644
--- a/src/gui/rhi/qrhi_p.h
+++ b/src/gui/rhi/qrhi_p.h
@@ -1420,6 +1420,8 @@ public:
static const int MAX_LAYERS = 6; // cubemaps only
static const int MAX_LEVELS = 16; // a width and/or height of 65536 should be enough for everyone
+ void releaseCachedResources();
+
protected:
QRhi();
diff --git a/src/gui/rhi/qrhi_p_p.h b/src/gui/rhi/qrhi_p_p.h
index 0914cf268b..b69757ae6d 100644
--- a/src/gui/rhi/qrhi_p_p.h
+++ b/src/gui/rhi/qrhi_p_p.h
@@ -157,6 +157,7 @@ public:
virtual const QRhiNativeHandles *nativeHandles() = 0;
virtual void sendVMemStatsToProfiler() = 0;
virtual void makeThreadLocalNativeContextCurrent() = 0;
+ virtual void releaseCachedResources() = 0;
bool isCompressedFormat(QRhiTexture::Format format) const;
void compressedFormatInfo(QRhiTexture::Format format, const QSize &size,
@@ -205,6 +206,8 @@ public:
QRhi *q;
+ static const int MAX_SHADER_CACHE_ENTRIES = 128;
+
protected:
bool debugMarkers = false;
int currentFrameSlot = 0; // for vk, mtl, and similar. unused by gl and d3d11.
diff --git a/src/gui/rhi/qrhid3d11.cpp b/src/gui/rhi/qrhid3d11.cpp
index 9e8533be23..93eadc047d 100644
--- a/src/gui/rhi/qrhid3d11.cpp
+++ b/src/gui/rhi/qrhid3d11.cpp
@@ -267,10 +267,20 @@ bool QRhiD3D11::create(QRhi::Flags flags)
return true;
}
+void QRhiD3D11::clearShaderCache()
+{
+ for (Shader &s : m_shaderCache)
+ s.s->Release();
+
+ m_shaderCache.clear();
+}
+
void QRhiD3D11::destroy()
{
finishActiveReadbacks();
+ clearShaderCache();
+
if (annotations) {
annotations->Release();
annotations = nullptr;
@@ -461,6 +471,11 @@ void QRhiD3D11::makeThreadLocalNativeContextCurrent()
// nothing to do here
}
+void QRhiD3D11::releaseCachedResources()
+{
+ clearShaderCache();
+}
+
QRhiRenderBuffer *QRhiD3D11::createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize,
int sampleCount, QRhiRenderBuffer::Flags flags)
{
@@ -3408,30 +3423,57 @@ bool QD3D11GraphicsPipeline::build()
QByteArray vsByteCode;
for (const QRhiShaderStage &shaderStage : qAsConst(m_shaderStages)) {
- QString error;
- QByteArray bytecode = compileHlslShaderSource(shaderStage.shader(), shaderStage.shaderVariant(), &error);
- if (bytecode.isEmpty()) {
- qWarning("HLSL shader compilation failed: %s", qPrintable(error));
- return false;
- }
- switch (shaderStage.type()) {
- case QRhiShaderStage::Vertex:
- hr = rhiD->dev->CreateVertexShader(bytecode.constData(), bytecode.size(), nullptr, &vs);
- if (FAILED(hr)) {
- qWarning("Failed to create vertex shader: %s", qPrintable(comErrorMessage(hr)));
- return false;
+ auto cacheIt = rhiD->m_shaderCache.constFind(shaderStage);
+ if (cacheIt != rhiD->m_shaderCache.constEnd()) {
+ switch (shaderStage.type()) {
+ case QRhiShaderStage::Vertex:
+ vs = static_cast<ID3D11VertexShader *>(cacheIt->s);
+ vs->AddRef();
+ vsByteCode = cacheIt->bytecode;
+ break;
+ case QRhiShaderStage::Fragment:
+ fs = static_cast<ID3D11PixelShader *>(cacheIt->s);
+ fs->AddRef();
+ break;
+ default:
+ break;
}
- vsByteCode = bytecode;
- break;
- case QRhiShaderStage::Fragment:
- hr = rhiD->dev->CreatePixelShader(bytecode.constData(), bytecode.size(), nullptr, &fs);
- if (FAILED(hr)) {
- qWarning("Failed to create pixel shader: %s", qPrintable(comErrorMessage(hr)));
+ } else {
+ QString error;
+ const QByteArray bytecode = compileHlslShaderSource(shaderStage.shader(), shaderStage.shaderVariant(), &error);
+ if (bytecode.isEmpty()) {
+ qWarning("HLSL shader compilation failed: %s", qPrintable(error));
return false;
}
- break;
- default:
- break;
+
+ if (rhiD->m_shaderCache.count() >= QRhiD3D11::MAX_SHADER_CACHE_ENTRIES) {
+ // Use the simplest strategy: too many cached shaders -> drop them all.
+ rhiD->clearShaderCache();
+ }
+
+ switch (shaderStage.type()) {
+ case QRhiShaderStage::Vertex:
+ hr = rhiD->dev->CreateVertexShader(bytecode.constData(), bytecode.size(), nullptr, &vs);
+ if (FAILED(hr)) {
+ qWarning("Failed to create vertex shader: %s", qPrintable(comErrorMessage(hr)));
+ return false;
+ }
+ vsByteCode = bytecode;
+ rhiD->m_shaderCache.insert(shaderStage, QRhiD3D11::Shader(vs, bytecode));
+ vs->AddRef();
+ break;
+ case QRhiShaderStage::Fragment:
+ hr = rhiD->dev->CreatePixelShader(bytecode.constData(), bytecode.size(), nullptr, &fs);
+ if (FAILED(hr)) {
+ qWarning("Failed to create pixel shader: %s", qPrintable(comErrorMessage(hr)));
+ return false;
+ }
+ rhiD->m_shaderCache.insert(shaderStage, QRhiD3D11::Shader(fs, bytecode));
+ fs->AddRef();
+ break;
+ default:
+ break;
+ }
}
}
@@ -3501,19 +3543,31 @@ bool QD3D11ComputePipeline::build()
QRHI_RES_RHI(QRhiD3D11);
- QString error;
- QByteArray bytecode = compileHlslShaderSource(m_shaderStage.shader(), m_shaderStage.shaderVariant(), &error);
- if (bytecode.isEmpty()) {
- qWarning("HLSL compute shader compilation failed: %s", qPrintable(error));
- return false;
- }
+ auto cacheIt = rhiD->m_shaderCache.constFind(m_shaderStage);
+ if (cacheIt != rhiD->m_shaderCache.constEnd()) {
+ cs = static_cast<ID3D11ComputeShader *>(cacheIt->s);
+ } else {
+ QString error;
+ const QByteArray bytecode = compileHlslShaderSource(m_shaderStage.shader(), m_shaderStage.shaderVariant(), &error);
+ if (bytecode.isEmpty()) {
+ qWarning("HLSL compute shader compilation failed: %s", qPrintable(error));
+ return false;
+ }
- HRESULT hr = rhiD->dev->CreateComputeShader(bytecode.constData(), bytecode.size(), nullptr, &cs);
- if (FAILED(hr)) {
- qWarning("Failed to create compute shader: %s", qPrintable(comErrorMessage(hr)));
- return false;
+ HRESULT hr = rhiD->dev->CreateComputeShader(bytecode.constData(), bytecode.size(), nullptr, &cs);
+ if (FAILED(hr)) {
+ qWarning("Failed to create compute shader: %s", qPrintable(comErrorMessage(hr)));
+ return false;
+ }
+
+ if (rhiD->m_shaderCache.count() >= QRhiD3D11::MAX_SHADER_CACHE_ENTRIES)
+ rhiD->clearShaderCache();
+
+ rhiD->m_shaderCache.insert(m_shaderStage, QRhiD3D11::Shader(cs, bytecode));
}
+ cs->AddRef();
+
generation += 1;
rhiD->registerResource(this);
return true;
diff --git a/src/gui/rhi/qrhid3d11_p_p.h b/src/gui/rhi/qrhid3d11_p_p.h
index 582146315d..cd44519aaa 100644
--- a/src/gui/rhi/qrhid3d11_p_p.h
+++ b/src/gui/rhi/qrhid3d11_p_p.h
@@ -632,6 +632,7 @@ public:
const QRhiNativeHandles *nativeHandles() override;
void sendVMemStatsToProfiler() override;
void makeThreadLocalNativeContextCurrent() override;
+ void releaseCachedResources() override;
void enqueueSubresUpload(QD3D11Texture *texD, QD3D11CommandBuffer *cbD,
int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc);
@@ -646,6 +647,7 @@ public:
DXGI_SAMPLE_DESC effectiveSampleCount(int sampleCount) const;
void finishActiveReadbacks();
void reportLiveObjects(ID3D11Device *device);
+ void clearShaderCache();
bool debugLayer = false;
bool importedDevice = false;
@@ -684,6 +686,14 @@ public:
QRhiTexture::Format format;
};
QVector<ActiveReadback> activeReadbacks;
+
+ struct Shader {
+ Shader() = default;
+ Shader(IUnknown *s, const QByteArray &bytecode) : s(s), bytecode(bytecode) { }
+ IUnknown *s;
+ QByteArray bytecode;
+ };
+ QHash<QRhiShaderStage, Shader> m_shaderCache;
};
Q_DECLARE_TYPEINFO(QRhiD3D11::ActiveReadback, Q_MOVABLE_TYPE);
diff --git a/src/gui/rhi/qrhigles2.cpp b/src/gui/rhi/qrhigles2.cpp
index f4e711e33e..9ad591a17a 100644
--- a/src/gui/rhi/qrhigles2.cpp
+++ b/src/gui/rhi/qrhigles2.cpp
@@ -502,6 +502,10 @@ void QRhiGles2::destroy()
ensureContext();
executeDeferredReleases();
+ for (uint shader : m_shaderCache)
+ f->glDeleteShader(shader);
+ m_shaderCache.clear();
+
if (!importedContext) {
delete ctx;
ctx = nullptr;
@@ -757,6 +761,17 @@ void QRhiGles2::makeThreadLocalNativeContextCurrent()
ensureContext();
}
+void QRhiGles2::releaseCachedResources()
+{
+ if (!ensureContext())
+ return;
+
+ for (uint shader : m_shaderCache)
+ f->glDeleteShader(shader);
+
+ m_shaderCache.clear();
+}
+
QRhiRenderBuffer *QRhiGles2::createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize,
int sampleCount, QRhiRenderBuffer::Flags flags)
{
@@ -2673,7 +2688,6 @@ static inline GLenum toGlShaderType(QRhiShaderStage::Type type)
bool QRhiGles2::compileShader(GLuint program, const QRhiShaderStage &shaderStage,
QShaderDescription *desc, int *glslVersionUsed)
{
- GLuint shader = f->glCreateShader(toGlShaderType(shaderStage.type()));
const QShader bakedShader = shaderStage.shader();
QVector<int> versionsToTry;
QByteArray source;
@@ -2733,27 +2747,40 @@ bool QRhiGles2::compileShader(GLuint program, const QRhiShaderStage &shaderStage
return false;
}
- const char *srcStr = source.constData();
- const GLint srcLength = source.count();
- f->glShaderSource(shader, 1, &srcStr, &srcLength);
- f->glCompileShader(shader);
- GLint compiled = 0;
- f->glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
- if (!compiled) {
- GLint infoLogLength = 0;
- f->glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLength);
- QByteArray log;
- if (infoLogLength > 1) {
- GLsizei length = 0;
- log.resize(infoLogLength);
- f->glGetShaderInfoLog(shader, infoLogLength, &length, log.data());
+ GLuint shader;
+ auto cacheIt = m_shaderCache.constFind(shaderStage);
+ if (cacheIt != m_shaderCache.constEnd()) {
+ shader = *cacheIt;
+ } else {
+ shader = f->glCreateShader(toGlShaderType(shaderStage.type()));
+ const char *srcStr = source.constData();
+ const GLint srcLength = source.count();
+ f->glShaderSource(shader, 1, &srcStr, &srcLength);
+ f->glCompileShader(shader);
+ GLint compiled = 0;
+ f->glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
+ if (!compiled) {
+ GLint infoLogLength = 0;
+ f->glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLength);
+ QByteArray log;
+ if (infoLogLength > 1) {
+ GLsizei length = 0;
+ log.resize(infoLogLength);
+ f->glGetShaderInfoLog(shader, infoLogLength, &length, log.data());
+ }
+ qWarning("Failed to compile shader: %s\nSource was:\n%s", log.constData(), source.constData());
+ return false;
}
- qWarning("Failed to compile shader: %s\nSource was:\n%s", log.constData(), source.constData());
- return false;
+ if (m_shaderCache.count() >= MAX_SHADER_CACHE_ENTRIES) {
+ // Use the simplest strategy: too many cached shaders -> drop them all.
+ for (uint shader : m_shaderCache)
+ f->glDeleteShader(shader); // does not actually get released yet when attached to a not-yet-released program
+ m_shaderCache.clear();
+ }
+ m_shaderCache.insert(shaderStage, shader);
}
f->glAttachShader(program, shader);
- f->glDeleteShader(shader);
*desc = bakedShader.description();
return true;
diff --git a/src/gui/rhi/qrhigles2_p_p.h b/src/gui/rhi/qrhigles2_p_p.h
index 6da529be92..877eb88d27 100644
--- a/src/gui/rhi/qrhigles2_p_p.h
+++ b/src/gui/rhi/qrhigles2_p_p.h
@@ -665,6 +665,7 @@ public:
const QRhiNativeHandles *nativeHandles() override;
void sendVMemStatsToProfiler() override;
void makeThreadLocalNativeContextCurrent() override;
+ void releaseCachedResources() override;
bool ensureContext(QSurface *surface = nullptr) const;
void executeDeferredReleases();
@@ -804,6 +805,8 @@ public:
bool active = false;
QGles2CommandBuffer cbWrapper;
} ofr;
+
+ QHash<QRhiShaderStage, uint> m_shaderCache;
};
Q_DECLARE_TYPEINFO(QRhiGles2::DeferredReleaseEntry, Q_MOVABLE_TYPE);
diff --git a/src/gui/rhi/qrhimetal.mm b/src/gui/rhi/qrhimetal.mm
index dfa79edb00..a14ffa7173 100644
--- a/src/gui/rhi/qrhimetal.mm
+++ b/src/gui/rhi/qrhimetal.mm
@@ -138,6 +138,20 @@ QT_BEGIN_NAMESPACE
\l{QRhiCommandBuffer::endPass()}.
*/
+struct QMetalShader
+{
+ id<MTLLibrary> lib = nil;
+ id<MTLFunction> func = nil;
+ std::array<uint, 3> localSize;
+
+ void release() {
+ [lib release];
+ lib = nil;
+ [func release];
+ func = nil;
+ }
+};
+
struct QRhiMetalData
{
QRhiMetalData(QRhiImplementation *rhi) : ofr(rhi) { }
@@ -206,6 +220,8 @@ struct QRhiMetalData
API_AVAILABLE(macos(10.13), ios(11.0)) id<MTLCaptureScope> captureScope = nil;
static const int TEXBUF_ALIGN = 256; // probably not accurate
+
+ QHash<QRhiShaderStage, QMetalShader> shaderCache;
};
Q_DECLARE_TYPEINFO(QRhiMetalData::DeferredReleaseEntry, Q_MOVABLE_TYPE);
@@ -289,17 +305,14 @@ struct QMetalGraphicsPipelineData
MTLPrimitiveType primitiveType;
MTLWinding winding;
MTLCullMode cullMode;
- id<MTLLibrary> vsLib = nil;
- id<MTLFunction> vsFunc = nil;
- id<MTLLibrary> fsLib = nil;
- id<MTLFunction> fsFunc = nil;
+ QMetalShader vs;
+ QMetalShader fs;
};
struct QMetalComputePipelineData
{
id<MTLComputePipelineState> ps = nil;
- id<MTLLibrary> csLib = nil;
- id<MTLFunction> csFunc = nil;
+ QMetalShader cs;
MTLSize localSize;
};
@@ -404,6 +417,10 @@ void QRhiMetal::destroy()
executeDeferredReleases(true);
finishActiveReadbacks(true);
+ for (QMetalShader &s : d->shaderCache)
+ s.release();
+ d->shaderCache.clear();
+
if (@available(macOS 10.13, iOS 11.0, *)) {
[d->captureScope release];
d->captureScope = nil;
@@ -570,6 +587,14 @@ void QRhiMetal::makeThreadLocalNativeContextCurrent()
// nothing to do here
}
+void QRhiMetal::releaseCachedResources()
+{
+ for (QMetalShader &s : d->shaderCache)
+ s.release();
+
+ d->shaderCache.clear();
+}
+
QRhiRenderBuffer *QRhiMetal::createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize,
int sampleCount, QRhiRenderBuffer::Flags flags)
{
@@ -2843,36 +2868,17 @@ void QMetalGraphicsPipeline::release()
{
QRHI_RES_RHI(QRhiMetal);
- if (!d->ps)
- return;
-
- if (d->ps) {
- [d->ps release];
- d->ps = nil;
- }
+ d->vs.release();
+ d->fs.release();
- if (d->ds) {
- [d->ds release];
- d->ds = nil;
- }
+ [d->ds release];
+ d->ds = nil;
- if (d->vsFunc) {
- [d->vsFunc release];
- d->vsFunc = nil;
- }
- if (d->vsLib) {
- [d->vsLib release];
- d->vsLib = nil;
- }
+ if (!d->ps)
+ return;
- if (d->fsFunc) {
- [d->fsFunc release];
- d->fsFunc = nil;
- }
- if (d->fsLib) {
- [d->fsLib release];
- d->fsLib = nil;
- }
+ [d->ps release];
+ d->ps = nil;
rhiD->unregisterResource(this);
}
@@ -3159,34 +3165,66 @@ bool QMetalGraphicsPipeline::build()
// buffers not just the resource binding layout) so leave it at the default
for (const QRhiShaderStage &shaderStage : qAsConst(m_shaderStages)) {
- QString error;
- QByteArray entryPoint;
- id<MTLLibrary> lib = rhiD->d->createMetalLib(shaderStage.shader(), shaderStage.shaderVariant(), &error, &entryPoint);
- if (!lib) {
- qWarning("MSL shader compilation failed: %s", qPrintable(error));
- return false;
- }
- id<MTLFunction> func = rhiD->d->createMSLShaderFunction(lib, entryPoint);
- if (!func) {
- qWarning("MSL function for entry point %s not found", entryPoint.constData());
- [lib release];
- return false;
- }
- switch (shaderStage.type()) {
- case QRhiShaderStage::Vertex:
- rpDesc.vertexFunction = func;
- d->vsLib = lib;
- d->vsFunc = func;
- break;
- case QRhiShaderStage::Fragment:
- rpDesc.fragmentFunction = func;
- d->fsLib = lib;
- d->fsFunc = func;
- break;
- default:
- [func release];
- [lib release];
- break;
+ auto cacheIt = rhiD->d->shaderCache.constFind(shaderStage);
+ if (cacheIt != rhiD->d->shaderCache.constEnd()) {
+ switch (shaderStage.type()) {
+ case QRhiShaderStage::Vertex:
+ d->vs = *cacheIt;
+ [d->vs.lib retain];
+ [d->vs.func retain];
+ rpDesc.vertexFunction = d->vs.func;
+ break;
+ case QRhiShaderStage::Fragment:
+ d->fs = *cacheIt;
+ [d->fs.lib retain];
+ [d->fs.func retain];
+ rpDesc.fragmentFunction = d->fs.func;
+ break;
+ default:
+ break;
+ }
+ } else {
+ QString error;
+ QByteArray entryPoint;
+ id<MTLLibrary> lib = rhiD->d->createMetalLib(shaderStage.shader(), shaderStage.shaderVariant(), &error, &entryPoint);
+ if (!lib) {
+ qWarning("MSL shader compilation failed: %s", qPrintable(error));
+ return false;
+ }
+ id<MTLFunction> func = rhiD->d->createMSLShaderFunction(lib, entryPoint);
+ if (!func) {
+ qWarning("MSL function for entry point %s not found", entryPoint.constData());
+ [lib release];
+ return false;
+ }
+ if (rhiD->d->shaderCache.count() >= QRhiMetal::MAX_SHADER_CACHE_ENTRIES) {
+ // Use the simplest strategy: too many cached shaders -> drop them all.
+ for (QMetalShader &s : rhiD->d->shaderCache)
+ s.release();
+ rhiD->d->shaderCache.clear();
+ }
+ switch (shaderStage.type()) {
+ case QRhiShaderStage::Vertex:
+ d->vs.lib = lib;
+ d->vs.func = func;
+ rhiD->d->shaderCache.insert(shaderStage, d->vs);
+ [d->vs.lib retain];
+ [d->vs.func retain];
+ rpDesc.vertexFunction = func;
+ break;
+ case QRhiShaderStage::Fragment:
+ d->fs.lib = lib;
+ d->fs.func = func;
+ rhiD->d->shaderCache.insert(shaderStage, d->fs);
+ [d->fs.lib retain];
+ [d->fs.func retain];
+ rpDesc.fragmentFunction = func;
+ break;
+ default:
+ [func release];
+ [lib release];
+ break;
+ }
}
}
@@ -3286,22 +3324,13 @@ void QMetalComputePipeline::release()
{
QRHI_RES_RHI(QRhiMetal);
- if (d->csFunc) {
- [d->csFunc release];
- d->csFunc = nil;
- }
- if (d->csLib) {
- [d->csLib release];
- d->csLib = nil;
- }
+ d->cs.release();
if (!d->ps)
return;
- if (d->ps) {
- [d->ps release];
- d->ps = nil;
- }
+ [d->ps release];
+ d->ps = nil;
rhiD->unregisterResource(this);
}
@@ -3313,28 +3342,44 @@ bool QMetalComputePipeline::build()
QRHI_RES_RHI(QRhiMetal);
- const QShader shader = m_shaderStage.shader();
- QString error;
- QByteArray entryPoint;
- id<MTLLibrary> lib = rhiD->d->createMetalLib(shader, m_shaderStage.shaderVariant(),
- &error, &entryPoint);
- if (!lib) {
- qWarning("MSL shader compilation failed: %s", qPrintable(error));
- return false;
- }
- id<MTLFunction> func = rhiD->d->createMSLShaderFunction(lib, entryPoint);
- if (!func) {
- qWarning("MSL function for entry point %s not found", entryPoint.constData());
- [lib release];
- return false;
+ auto cacheIt = rhiD->d->shaderCache.constFind(m_shaderStage);
+ if (cacheIt != rhiD->d->shaderCache.constEnd()) {
+ d->cs = *cacheIt;
+ } else {
+ const QShader shader = m_shaderStage.shader();
+ QString error;
+ QByteArray entryPoint;
+ id<MTLLibrary> lib = rhiD->d->createMetalLib(shader, m_shaderStage.shaderVariant(),
+ &error, &entryPoint);
+ if (!lib) {
+ qWarning("MSL shader compilation failed: %s", qPrintable(error));
+ return false;
+ }
+ id<MTLFunction> func = rhiD->d->createMSLShaderFunction(lib, entryPoint);
+ if (!func) {
+ qWarning("MSL function for entry point %s not found", entryPoint.constData());
+ [lib release];
+ return false;
+ }
+ d->cs.lib = lib;
+ d->cs.func = func;
+ d->cs.localSize = shader.description().computeShaderLocalSize();
+
+ if (rhiD->d->shaderCache.count() >= QRhiMetal::MAX_SHADER_CACHE_ENTRIES) {
+ for (QMetalShader &s : rhiD->d->shaderCache)
+ s.release();
+ rhiD->d->shaderCache.clear();
+ }
+ rhiD->d->shaderCache.insert(m_shaderStage, d->cs);
}
- d->csLib = lib;
- d->csFunc = func;
- std::array<uint, 3> localSize = shader.description().computeShaderLocalSize();
- d->localSize = MTLSizeMake(localSize[0], localSize[1], localSize[2]);
+
+ [d->cs.lib retain];
+ [d->cs.func retain];
+
+ d->localSize = MTLSizeMake(d->cs.localSize[0], d->cs.localSize[1], d->cs.localSize[2]);
NSError *err = nil;
- d->ps = [rhiD->d->dev newComputePipelineStateWithFunction: d->csFunc error: &err];
+ d->ps = [rhiD->d->dev newComputePipelineStateWithFunction: d->cs.func error: &err];
if (!d->ps) {
const QString msg = QString::fromNSString(err.localizedDescription);
qWarning("Failed to create render pipeline state: %s", qPrintable(msg));
diff --git a/src/gui/rhi/qrhimetal_p_p.h b/src/gui/rhi/qrhimetal_p_p.h
index c448865f4d..01b0bf4f56 100644
--- a/src/gui/rhi/qrhimetal_p_p.h
+++ b/src/gui/rhi/qrhimetal_p_p.h
@@ -417,6 +417,7 @@ public:
const QRhiNativeHandles *nativeHandles() override;
void sendVMemStatsToProfiler() override;
void makeThreadLocalNativeContextCurrent() override;
+ void releaseCachedResources() override;
void executeDeferredReleases(bool forced = false);
void finishActiveReadbacks(bool forced = false);
diff --git a/src/gui/rhi/qrhinull.cpp b/src/gui/rhi/qrhinull.cpp
index dff6e05268..29a3968bfc 100644
--- a/src/gui/rhi/qrhinull.cpp
+++ b/src/gui/rhi/qrhinull.cpp
@@ -174,6 +174,11 @@ void QRhiNull::makeThreadLocalNativeContextCurrent()
// nothing to do here
}
+void QRhiNull::releaseCachedResources()
+{
+ // nothing to do here
+}
+
QRhiRenderBuffer *QRhiNull::createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize,
int sampleCount, QRhiRenderBuffer::Flags flags)
{
diff --git a/src/gui/rhi/qrhinull_p_p.h b/src/gui/rhi/qrhinull_p_p.h
index bdf0d59724..b43f830d5e 100644
--- a/src/gui/rhi/qrhinull_p_p.h
+++ b/src/gui/rhi/qrhinull_p_p.h
@@ -283,6 +283,7 @@ public:
const QRhiNativeHandles *nativeHandles() override;
void sendVMemStatsToProfiler() override;
void makeThreadLocalNativeContextCurrent() override;
+ void releaseCachedResources() override;
QRhiNullNativeHandles nativeHandlesStruct;
QRhiSwapChain *currentSwapChain = nullptr;
diff --git a/src/gui/rhi/qrhivulkan.cpp b/src/gui/rhi/qrhivulkan.cpp
index dfc85fb853..4f550c6a90 100644
--- a/src/gui/rhi/qrhivulkan.cpp
+++ b/src/gui/rhi/qrhivulkan.cpp
@@ -3744,6 +3744,11 @@ void QRhiVulkan::makeThreadLocalNativeContextCurrent()
// nothing to do here
}
+void QRhiVulkan::releaseCachedResources()
+{
+ // nothing to do here
+}
+
QRhiRenderBuffer *QRhiVulkan::createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize,
int sampleCount, QRhiRenderBuffer::Flags flags)
{
diff --git a/src/gui/rhi/qrhivulkan_p_p.h b/src/gui/rhi/qrhivulkan_p_p.h
index 962a1b8eb7..23cc80b814 100644
--- a/src/gui/rhi/qrhivulkan_p_p.h
+++ b/src/gui/rhi/qrhivulkan_p_p.h
@@ -712,6 +712,7 @@ public:
const QRhiNativeHandles *nativeHandles() override;
void sendVMemStatsToProfiler() override;
void makeThreadLocalNativeContextCurrent() override;
+ void releaseCachedResources() override;
VkResult createDescriptorPool(VkDescriptorPool *pool);
bool allocateDescriptorSet(VkDescriptorSetAllocateInfo *allocInfo, VkDescriptorSet *result, int *resultPoolIndex);