From cc631c15a00f26fa8f6aa703fd7cf9e715be8f3c Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Tue, 22 Oct 2019 12:37:34 +0200 Subject: [PATCH] rhi: metal: Remap resource bindings based on the QShader table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ...when available. Fall back to the QRhi (i.e. SPIR-V) binding point otherwise (which becomes unsafe once shadertools bumps its SPIRV-Cross snapshot, but is fine for existing .qsb files) Task-number: QTBUG-79368 Change-Id: I2d452fdd4efb484867732c358171a800d3261dcd Reviewed-by: Christian Strømme --- src/gui/rhi/qrhimetal.mm | 97 ++++++++++++++++++++++++++----------- src/gui/rhi/qrhimetal_p_p.h | 7 ++- 2 files changed, 73 insertions(+), 31 deletions(-) diff --git a/src/gui/rhi/qrhimetal.mm b/src/gui/rhi/qrhimetal.mm index 5f14d917b8..b2d56b43af 100644 --- a/src/gui/rhi/qrhimetal.mm +++ b/src/gui/rhi/qrhimetal.mm @@ -35,8 +35,6 @@ ****************************************************************************/ #include "qrhimetal_p_p.h" -#include "qshader_p.h" -#include "qshaderdescription_p.h" #include #include #include @@ -143,8 +141,10 @@ struct QMetalShader id lib = nil; id func = nil; std::array localSize; + QShader::NativeResourceBindingMap nativeResourceBindingMap; void release() { + nativeResourceBindingMap.clear(); [lib release]; lib = nil; [func release]; @@ -164,7 +164,7 @@ struct QRhiMetalData const QRhiDepthStencilClearValue &depthStencilClearValue, int colorAttCount); id createMetalLib(const QShader &shader, QShader::Variant shaderVariant, - QString *error, QByteArray *entryPoint); + QString *error, QByteArray *entryPoint, QShaderKey *activeKey); id createMSLShaderFunction(id lib, const QByteArray &entryPoint); struct DeferredReleaseEntry { @@ -653,18 +653,39 @@ QRhiShaderResourceBindings *QRhiMetal::createShaderResourceBindings() return new QMetalShaderResourceBindings(this); } -void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD, QMetalCommandBuffer *cbD, +enum class BindingType { + Buffer, + Texture, + Sampler +}; + +static inline int mapBinding(int binding, + int stageIndex, + const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[], + BindingType type) +{ + const QShader::NativeResourceBindingMap *map = nativeResourceBindingMaps[stageIndex]; + if (map) { + auto it = map->constFind(binding); + if (it != map->cend()) + return type == BindingType::Sampler ? it->second : it->first; + } + return binding; +} + +void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD, + QMetalCommandBuffer *cbD, int dynamicOffsetCount, const QRhiCommandBuffer::DynamicOffset *dynamicOffsets, - bool offsetOnlyChange) + bool offsetOnlyChange, + const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[SUPPORTED_STAGES]) { - static const int KNOWN_STAGES = 3; struct { QRhiBatchedBindings > buffers; QRhiBatchedBindings bufferOffsets; QRhiBatchedBindings > textures; QRhiBatchedBindings > samplers; - } res[KNOWN_STAGES]; + } res[SUPPORTED_STAGES]; for (const QRhiShaderResourceBinding &binding : qAsConst(srbD->sortedBindings)) { const QRhiShaderResourceBinding::Data *b = binding.data(); @@ -682,15 +703,15 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD } } if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) { - res[0].buffers.feed(b->binding, mtlbuf); + res[0].buffers.feed(mapBinding(b->binding, 0, nativeResourceBindingMaps, BindingType::Buffer), mtlbuf); res[0].bufferOffsets.feed(b->binding, offset); } if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) { - res[1].buffers.feed(b->binding, mtlbuf); + res[1].buffers.feed(mapBinding(b->binding, 1, nativeResourceBindingMaps, BindingType::Buffer), mtlbuf); res[1].bufferOffsets.feed(b->binding, offset); } if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) { - res[2].buffers.feed(b->binding, mtlbuf); + res[2].buffers.feed(mapBinding(b->binding, 2, nativeResourceBindingMaps, BindingType::Buffer), mtlbuf); res[2].bufferOffsets.feed(b->binding, offset); } } @@ -700,16 +721,16 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD QMetalTexture *texD = QRHI_RES(QMetalTexture, b->u.stex.tex); QMetalSampler *samplerD = QRHI_RES(QMetalSampler, b->u.stex.sampler); if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) { - res[0].textures.feed(b->binding, texD->d->tex); - res[0].samplers.feed(b->binding, samplerD->d->samplerState); + res[0].textures.feed(mapBinding(b->binding, 0, nativeResourceBindingMaps, BindingType::Texture), texD->d->tex); + res[0].samplers.feed(mapBinding(b->binding, 0, nativeResourceBindingMaps, BindingType::Sampler), samplerD->d->samplerState); } if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) { - res[1].textures.feed(b->binding, texD->d->tex); - res[1].samplers.feed(b->binding, samplerD->d->samplerState); + res[1].textures.feed(mapBinding(b->binding, 1, nativeResourceBindingMaps, BindingType::Texture), texD->d->tex); + res[1].samplers.feed(mapBinding(b->binding, 1, nativeResourceBindingMaps, BindingType::Sampler), samplerD->d->samplerState); } if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) { - res[2].textures.feed(b->binding, texD->d->tex); - res[2].samplers.feed(b->binding, samplerD->d->samplerState); + res[2].textures.feed(mapBinding(b->binding, 2, nativeResourceBindingMaps, BindingType::Texture), texD->d->tex); + res[2].samplers.feed(mapBinding(b->binding, 2, nativeResourceBindingMaps, BindingType::Sampler), samplerD->d->samplerState); } } break; @@ -722,11 +743,11 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD QMetalTexture *texD = QRHI_RES(QMetalTexture, b->u.simage.tex); id t = texD->d->viewForLevel(b->u.simage.level); if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) - res[0].textures.feed(b->binding, t); + res[0].textures.feed(mapBinding(b->binding, 0, nativeResourceBindingMaps, BindingType::Texture), t); if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) - res[1].textures.feed(b->binding, t); + res[1].textures.feed(mapBinding(b->binding, 1, nativeResourceBindingMaps, BindingType::Texture), t); if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) - res[2].textures.feed(b->binding, t); + res[2].textures.feed(mapBinding(b->binding, 2, nativeResourceBindingMaps, BindingType::Texture), t); } break; case QRhiShaderResourceBinding::BufferLoad: @@ -739,15 +760,15 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD id mtlbuf = bufD->d->buf[0]; uint offset = uint(b->u.sbuf.offset); if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) { - res[0].buffers.feed(b->binding, mtlbuf); + res[0].buffers.feed(mapBinding(b->binding, 0, nativeResourceBindingMaps, BindingType::Buffer), mtlbuf); res[0].bufferOffsets.feed(b->binding, offset); } if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) { - res[1].buffers.feed(b->binding, mtlbuf); + res[1].buffers.feed(mapBinding(b->binding, 1, nativeResourceBindingMaps, BindingType::Buffer), mtlbuf); res[1].bufferOffsets.feed(b->binding, offset); } if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) { - res[2].buffers.feed(b->binding, mtlbuf); + res[2].buffers.feed(mapBinding(b->binding, 2, nativeResourceBindingMaps, BindingType::Buffer), mtlbuf); res[2].bufferOffsets.feed(b->binding, offset); } } @@ -758,7 +779,7 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD } } - for (int idx = 0; idx < KNOWN_STAGES; ++idx) { + for (int idx = 0; idx < SUPPORTED_STAGES; ++idx) { res[idx].buffers.finish(); res[idx].bufferOffsets.finish(); @@ -973,18 +994,22 @@ void QRhiMetal::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBind // dynamic uniform buffer offsets always trigger a rebind if (hasDynamicOffsetInSrb || resNeedsRebind || srbChanged || srbRebuilt) { + const QShader::NativeResourceBindingMap *resBindMaps[SUPPORTED_STAGES] = { nullptr, nullptr, nullptr }; if (gfxPsD) { cbD->currentGraphicsSrb = srb; cbD->currentComputeSrb = nullptr; + resBindMaps[0] = &gfxPsD->d->vs.nativeResourceBindingMap; + resBindMaps[1] = &gfxPsD->d->fs.nativeResourceBindingMap; } else { cbD->currentGraphicsSrb = nullptr; cbD->currentComputeSrb = srb; + resBindMaps[2] = &compPsD->d->cs.nativeResourceBindingMap; } cbD->currentSrbGeneration = srbD->generation; cbD->currentResSlot = resSlot; const bool offsetOnlyChange = hasDynamicOffsetInSrb && !resNeedsRebind && !srbChanged && !srbRebuilt; - enqueueShaderResourceBindings(srbD, cbD, dynamicOffsetCount, dynamicOffsets, offsetOnlyChange); + enqueueShaderResourceBindings(srbD, cbD, dynamicOffsetCount, dynamicOffsets, offsetOnlyChange, resBindMaps); } } @@ -3081,9 +3106,10 @@ static inline MTLCullMode toMetalCullMode(QRhiGraphicsPipeline::CullMode c) } id QRhiMetalData::createMetalLib(const QShader &shader, QShader::Variant shaderVariant, - QString *error, QByteArray *entryPoint) + QString *error, QByteArray *entryPoint, QShaderKey *activeKey) { - QShaderCode mtllib = shader.shader({ QShader::MetalLibShader, 12, shaderVariant }); + QShaderKey key = { QShader::MetalLibShader, 12, shaderVariant }; + QShaderCode mtllib = shader.shader(key); if (!mtllib.shader().isEmpty()) { dispatch_data_t data = dispatch_data_create(mtllib.shader().constData(), size_t(mtllib.shader().size()), @@ -3094,6 +3120,7 @@ id QRhiMetalData::createMetalLib(const QShader &shader, QShader::Var dispatch_release(data); if (!err) { *entryPoint = mtllib.entryPoint(); + *activeKey = key; return lib; } else { const QString msg = QString::fromNSString(err.localizedDescription); @@ -3101,7 +3128,8 @@ id QRhiMetalData::createMetalLib(const QShader &shader, QShader::Var } } - QShaderCode mslSource = shader.shader({ QShader::MslShader, 12, shaderVariant }); + key = { QShader::MslShader, 12, shaderVariant }; + QShaderCode mslSource = shader.shader(key); if (mslSource.shader().isEmpty()) { qWarning() << "No MSL 1.2 code found in baked shader" << shader; return nil; @@ -3122,6 +3150,7 @@ id QRhiMetalData::createMetalLib(const QShader &shader, QShader::Var } *entryPoint = mslSource.entryPoint(); + *activeKey = key; return lib; } @@ -3195,9 +3224,12 @@ bool QMetalGraphicsPipeline::build() break; } } else { + const QShader shader = shaderStage.shader(); QString error; QByteArray entryPoint; - id lib = rhiD->d->createMetalLib(shaderStage.shader(), shaderStage.shaderVariant(), &error, &entryPoint); + QShaderKey activeKey; + id lib = rhiD->d->createMetalLib(shader, shaderStage.shaderVariant(), + &error, &entryPoint, &activeKey); if (!lib) { qWarning("MSL shader compilation failed: %s", qPrintable(error)); return false; @@ -3218,6 +3250,8 @@ bool QMetalGraphicsPipeline::build() case QRhiShaderStage::Vertex: d->vs.lib = lib; d->vs.func = func; + if (const QShader::NativeResourceBindingMap *map = shader.nativeResourceBindingMap(activeKey)) + d->vs.nativeResourceBindingMap = *map; rhiD->d->shaderCache.insert(shaderStage, d->vs); [d->vs.lib retain]; [d->vs.func retain]; @@ -3226,6 +3260,8 @@ bool QMetalGraphicsPipeline::build() case QRhiShaderStage::Fragment: d->fs.lib = lib; d->fs.func = func; + if (const QShader::NativeResourceBindingMap *map = shader.nativeResourceBindingMap(activeKey)) + d->fs.nativeResourceBindingMap = *map; rhiD->d->shaderCache.insert(shaderStage, d->fs); [d->fs.lib retain]; [d->fs.func retain]; @@ -3360,8 +3396,9 @@ bool QMetalComputePipeline::build() const QShader shader = m_shaderStage.shader(); QString error; QByteArray entryPoint; + QShaderKey activeKey; id lib = rhiD->d->createMetalLib(shader, m_shaderStage.shaderVariant(), - &error, &entryPoint); + &error, &entryPoint, &activeKey); if (!lib) { qWarning("MSL shader compilation failed: %s", qPrintable(error)); return false; @@ -3375,6 +3412,8 @@ bool QMetalComputePipeline::build() d->cs.lib = lib; d->cs.func = func; d->cs.localSize = shader.description().computeShaderLocalSize(); + if (const QShader::NativeResourceBindingMap *map = shader.nativeResourceBindingMap(activeKey)) + d->cs.nativeResourceBindingMap = *map; if (rhiD->d->shaderCache.count() >= QRhiMetal::MAX_SHADER_CACHE_ENTRIES) { for (QMetalShader &s : rhiD->d->shaderCache) diff --git a/src/gui/rhi/qrhimetal_p_p.h b/src/gui/rhi/qrhimetal_p_p.h index 688fec8147..2be86db5c8 100644 --- a/src/gui/rhi/qrhimetal_p_p.h +++ b/src/gui/rhi/qrhimetal_p_p.h @@ -433,10 +433,13 @@ public: qsizetype *curOfs); void enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates); void executeBufferHostWritesForCurrentFrame(QMetalBuffer *bufD); - void enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD, QMetalCommandBuffer *cbD, + static const int SUPPORTED_STAGES = 3; + void enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD, + QMetalCommandBuffer *cbD, int dynamicOffsetCount, const QRhiCommandBuffer::DynamicOffset *dynamicOffsets, - bool offsetOnlyChange); + bool offsetOnlyChange, + const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[SUPPORTED_STAGES]); int effectiveSampleCount(int sampleCount) const; bool importedDevice = false;