summaryrefslogtreecommitdiffstats
path: root/src/gui/rhi
diff options
context:
space:
mode:
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);