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>bb10
parent
89d0a03c06
commit
7af6649e88
|
|
@ -5019,6 +5019,23 @@ QRhiProfiler *QRhi::profiler()
|
|||
return &d->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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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)));
|
||||
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;
|
||||
}
|
||||
} 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;
|
||||
}
|
||||
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)));
|
||||
return false;
|
||||
|
||||
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;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3501,18 +3543,30 @@ 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;
|
||||
}
|
||||
|
||||
if (rhiD->m_shaderCache.count() >= QRhiD3D11::MAX_SHADER_CACHE_ENTRIES)
|
||||
rhiD->clearShaderCache();
|
||||
|
||||
rhiD->m_shaderCache.insert(m_shaderStage, QRhiD3D11::Shader(cs, bytecode));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
cs->AddRef();
|
||||
|
||||
generation += 1;
|
||||
rhiD->registerResource(this);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
d->vs.release();
|
||||
d->fs.release();
|
||||
|
||||
[d->ds release];
|
||||
d->ds = nil;
|
||||
|
||||
if (!d->ps)
|
||||
return;
|
||||
|
||||
if (d->ps) {
|
||||
[d->ps release];
|
||||
d->ps = nil;
|
||||
}
|
||||
|
||||
if (d->ds) {
|
||||
[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->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;
|
||||
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);
|
||||
}
|
||||
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->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));
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -283,6 +283,7 @@ public:
|
|||
const QRhiNativeHandles *nativeHandles() override;
|
||||
void sendVMemStatsToProfiler() override;
|
||||
void makeThreadLocalNativeContextCurrent() override;
|
||||
void releaseCachedResources() override;
|
||||
|
||||
QRhiNullNativeHandles nativeHandlesStruct;
|
||||
QRhiSwapChain *currentSwapChain = nullptr;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue