RHI: Cache ShaderResourceBindings for Metal

We current have lots of redundant state bindings for things like
SamplerState that could reduced by checking if the state is already set.
ShaderResourceBindings are fairly involved to cache since they are done
in batches. This patch stores the current ShaderResourceBindings state
and checkes the current state before setting a new one.

Change-Id: If15ebd2b8b14b016f492d881197e423773c30ef7
Reviewed-by: Laszlo Agocs <laszlo.agocs@qt.io>
This commit is contained in:
Andy Nichols 2022-08-04 13:56:24 +02:00 committed by Andy Nichols
parent 0c716c687a
commit ea1f172267

View File

@ -232,6 +232,32 @@ struct QMetalSamplerData
id<MTLSamplerState> samplerState = nil; id<MTLSamplerState> samplerState = nil;
}; };
struct QMetalShaderResourceBindingsData {
struct Stage {
struct Buffer {
int nativeBinding;
id<MTLBuffer> mtlbuf;
quint32 offset;
};
struct Texture {
int nativeBinding;
id<MTLTexture> mtltex;
};
struct Sampler {
int nativeBinding;
id<MTLSamplerState> mtlsampler;
};
QVarLengthArray<Buffer, 8> buffers;
QVarLengthArray<Texture, 8> textures;
QVarLengthArray<Sampler, 8> samplers;
QRhiBatchedBindings<id<MTLBuffer> > bufferBatches;
QRhiBatchedBindings<NSUInteger> bufferOffsetBatches;
QRhiBatchedBindings<id<MTLTexture> > textureBatches;
QRhiBatchedBindings<id<MTLSamplerState> > samplerBatches;
} res[QRhiMetal::SUPPORTED_STAGES];
enum { VERTEX = 0, FRAGMENT = 1, COMPUTE = 2 };
};
struct QMetalCommandBufferData struct QMetalCommandBufferData
{ {
id<MTLCommandBuffer> cb; id<MTLCommandBuffer> cb;
@ -242,6 +268,7 @@ struct QMetalCommandBufferData
QRhiBatchedBindings<id<MTLBuffer> > currentVertexInputsBuffers; QRhiBatchedBindings<id<MTLBuffer> > currentVertexInputsBuffers;
QRhiBatchedBindings<NSUInteger> currentVertexInputOffsets; QRhiBatchedBindings<NSUInteger> currentVertexInputOffsets;
id<MTLDepthStencilState> currentDepthStencilState; id<MTLDepthStencilState> currentDepthStencilState;
QMetalShaderResourceBindingsData currentShaderResourceBindingState;
}; };
struct QMetalRenderTargetData struct QMetalRenderTargetData
@ -792,6 +819,46 @@ static inline int mapBinding(int binding,
return -1; return -1;
} }
static inline bool areBufferBatchesEqual(const QRhiBatchedBindings<id<MTLBuffer> >::Batch &batch1,
const QRhiBatchedBindings<NSUInteger>::Batch &offsetBatch1,
const QRhiBatchedBindings<id<MTLBuffer> >::Batch &batch2,
const QRhiBatchedBindings<NSUInteger>::Batch &offsetBatch2)
{
if (batch1.startBinding != batch2.startBinding)
return false;
if (batch1.resources.count() != batch2.resources.count())
return false;
if (batch1 != batch2)
return false;
if (offsetBatch1 != offsetBatch2)
return false;
return true;
}
static inline bool areTextureBatchesEqual(const QRhiBatchedBindings<id<MTLTexture> >::Batch &batch1,
const QRhiBatchedBindings<id<MTLTexture> >::Batch &batch2)
{
if (batch1.startBinding != batch2.startBinding)
return false;
if (batch1.resources.count() != batch2.resources.count())
return false;
if (batch1 != batch2)
return false;
return true;
};
static inline bool areSamplerBatchesEqual(const QRhiBatchedBindings<id<MTLSamplerState> >::Batch &batch1,
const QRhiBatchedBindings<id<MTLSamplerState> >::Batch &batch2)
{
if (batch1.startBinding != batch2.startBinding)
return false;
if (batch1.resources.count() != batch2.resources.count())
return false;
if (batch1 != batch2)
return false;
return true;
};
void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD, void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD,
QMetalCommandBuffer *cbD, QMetalCommandBuffer *cbD,
int dynamicOffsetCount, int dynamicOffsetCount,
@ -799,29 +866,7 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD
bool offsetOnlyChange, bool offsetOnlyChange,
const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[SUPPORTED_STAGES]) const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[SUPPORTED_STAGES])
{ {
struct Stage { QMetalShaderResourceBindingsData bindingData;
struct Buffer {
int nativeBinding;
id<MTLBuffer> mtlbuf;
quint32 offset;
};
struct Texture {
int nativeBinding;
id<MTLTexture> mtltex;
};
struct Sampler {
int nativeBinding;
id<MTLSamplerState> mtlsampler;
};
QVarLengthArray<Buffer, 8> buffers;
QVarLengthArray<Texture, 8> textures;
QVarLengthArray<Sampler, 8> samplers;
QRhiBatchedBindings<id<MTLBuffer> > bufferBatches;
QRhiBatchedBindings<NSUInteger> bufferOffsetBatches;
QRhiBatchedBindings<id<MTLTexture> > textureBatches;
QRhiBatchedBindings<id<MTLSamplerState> > samplerBatches;
} res[SUPPORTED_STAGES];
enum { VERTEX = 0, FRAGMENT = 1, COMPUTE = 2 };
for (const QRhiShaderResourceBinding &binding : qAsConst(srbD->sortedBindings)) { for (const QRhiShaderResourceBinding &binding : qAsConst(srbD->sortedBindings)) {
const QRhiShaderResourceBinding::Data *b = binding.data(); const QRhiShaderResourceBinding::Data *b = binding.data();
@ -839,19 +884,19 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD
} }
} }
if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) { if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) {
const int nativeBinding = mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Buffer); const int nativeBinding = mapBinding(b->binding, QMetalShaderResourceBindingsData::VERTEX, nativeResourceBindingMaps, BindingType::Buffer);
if (nativeBinding >= 0) if (nativeBinding >= 0)
res[VERTEX].buffers.append({ nativeBinding, mtlbuf, offset }); bindingData.res[QMetalShaderResourceBindingsData::VERTEX].buffers.append({ nativeBinding, mtlbuf, offset });
} }
if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) { if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
const int nativeBinding = mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Buffer); const int nativeBinding = mapBinding(b->binding, QMetalShaderResourceBindingsData::FRAGMENT, nativeResourceBindingMaps, BindingType::Buffer);
if (nativeBinding >= 0) if (nativeBinding >= 0)
res[FRAGMENT].buffers.append({ nativeBinding, mtlbuf, offset }); bindingData.res[QMetalShaderResourceBindingsData::FRAGMENT].buffers.append({ nativeBinding, mtlbuf, offset });
} }
if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) { if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
const int nativeBinding = mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Buffer); const int nativeBinding = mapBinding(b->binding, QMetalShaderResourceBindingsData::COMPUTE, nativeResourceBindingMaps, BindingType::Buffer);
if (nativeBinding >= 0) if (nativeBinding >= 0)
res[COMPUTE].buffers.append({ nativeBinding, mtlbuf, offset }); bindingData.res[QMetalShaderResourceBindingsData::COMPUTE].buffers.append({ nativeBinding, mtlbuf, offset });
} }
} }
break; break;
@ -868,31 +913,31 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD
// first = texture binding, second = sampler binding // first = texture binding, second = sampler binding
// first = texture binding // first = texture binding
// first = sampler binding (i.e. BindingType::Texture...) // first = sampler binding (i.e. BindingType::Texture...)
const int textureBinding = mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Texture); const int textureBinding = mapBinding(b->binding, QMetalShaderResourceBindingsData::VERTEX, nativeResourceBindingMaps, BindingType::Texture);
const int samplerBinding = texD && samplerD ? mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Sampler) const int samplerBinding = texD && samplerD ? mapBinding(b->binding, QMetalShaderResourceBindingsData::VERTEX, nativeResourceBindingMaps, BindingType::Sampler)
: (samplerD ? mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Texture) : -1); : (samplerD ? mapBinding(b->binding, QMetalShaderResourceBindingsData::VERTEX, nativeResourceBindingMaps, BindingType::Texture) : -1);
if (textureBinding >= 0 && texD) if (textureBinding >= 0 && texD)
res[VERTEX].textures.append({ textureBinding + elem, texD->d->tex }); bindingData.res[QMetalShaderResourceBindingsData::VERTEX].textures.append({ textureBinding + elem, texD->d->tex });
if (samplerBinding >= 0) if (samplerBinding >= 0)
res[VERTEX].samplers.append({ samplerBinding + elem, samplerD->d->samplerState }); bindingData.res[QMetalShaderResourceBindingsData::VERTEX].samplers.append({ samplerBinding + elem, samplerD->d->samplerState });
} }
if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) { if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
const int textureBinding = mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Texture); const int textureBinding = mapBinding(b->binding, QMetalShaderResourceBindingsData::FRAGMENT, nativeResourceBindingMaps, BindingType::Texture);
const int samplerBinding = texD && samplerD ? mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Sampler) const int samplerBinding = texD && samplerD ? mapBinding(b->binding, QMetalShaderResourceBindingsData::FRAGMENT, nativeResourceBindingMaps, BindingType::Sampler)
: (samplerD ? mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Texture) : -1); : (samplerD ? mapBinding(b->binding, QMetalShaderResourceBindingsData::FRAGMENT, nativeResourceBindingMaps, BindingType::Texture) : -1);
if (textureBinding >= 0 && texD) if (textureBinding >= 0 && texD)
res[FRAGMENT].textures.append({ textureBinding + elem, texD->d->tex }); bindingData.res[QMetalShaderResourceBindingsData::FRAGMENT].textures.append({ textureBinding + elem, texD->d->tex });
if (samplerBinding >= 0) if (samplerBinding >= 0)
res[FRAGMENT].samplers.append({ samplerBinding + elem, samplerD->d->samplerState }); bindingData.res[QMetalShaderResourceBindingsData::FRAGMENT].samplers.append({ samplerBinding + elem, samplerD->d->samplerState });
} }
if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) { if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
const int textureBinding = mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Texture); const int textureBinding = mapBinding(b->binding, QMetalShaderResourceBindingsData::COMPUTE, nativeResourceBindingMaps, BindingType::Texture);
const int samplerBinding = texD && samplerD ? mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Sampler) const int samplerBinding = texD && samplerD ? mapBinding(b->binding, QMetalShaderResourceBindingsData::COMPUTE, nativeResourceBindingMaps, BindingType::Sampler)
: (samplerD ? mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Texture) : -1); : (samplerD ? mapBinding(b->binding, QMetalShaderResourceBindingsData::COMPUTE, nativeResourceBindingMaps, BindingType::Texture) : -1);
if (textureBinding >= 0 && texD) if (textureBinding >= 0 && texD)
res[COMPUTE].textures.append({ textureBinding + elem, texD->d->tex }); bindingData.res[QMetalShaderResourceBindingsData::COMPUTE].textures.append({ textureBinding + elem, texD->d->tex });
if (samplerBinding >= 0) if (samplerBinding >= 0)
res[COMPUTE].samplers.append({ samplerBinding + elem, samplerD->d->samplerState }); bindingData.res[QMetalShaderResourceBindingsData::COMPUTE].samplers.append({ samplerBinding + elem, samplerD->d->samplerState });
} }
} }
} }
@ -904,19 +949,19 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD
QMetalTexture *texD = QRHI_RES(QMetalTexture, b->u.simage.tex); QMetalTexture *texD = QRHI_RES(QMetalTexture, b->u.simage.tex);
id<MTLTexture> t = texD->d->viewForLevel(b->u.simage.level); id<MTLTexture> t = texD->d->viewForLevel(b->u.simage.level);
if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) { if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) {
const int nativeBinding = mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Texture); const int nativeBinding = mapBinding(b->binding, QMetalShaderResourceBindingsData::VERTEX, nativeResourceBindingMaps, BindingType::Texture);
if (nativeBinding >= 0) if (nativeBinding >= 0)
res[VERTEX].textures.append({ nativeBinding, t }); bindingData.res[QMetalShaderResourceBindingsData::VERTEX].textures.append({ nativeBinding, t });
} }
if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) { if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
const int nativeBinding = mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Texture); const int nativeBinding = mapBinding(b->binding, QMetalShaderResourceBindingsData::FRAGMENT, nativeResourceBindingMaps, BindingType::Texture);
if (nativeBinding >= 0) if (nativeBinding >= 0)
res[FRAGMENT].textures.append({ nativeBinding, t }); bindingData.res[QMetalShaderResourceBindingsData::FRAGMENT].textures.append({ nativeBinding, t });
} }
if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) { if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
const int nativeBinding = mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Texture); const int nativeBinding = mapBinding(b->binding, QMetalShaderResourceBindingsData::COMPUTE, nativeResourceBindingMaps, BindingType::Texture);
if (nativeBinding >= 0) if (nativeBinding >= 0)
res[COMPUTE].textures.append({ nativeBinding, t }); bindingData.res[QMetalShaderResourceBindingsData::COMPUTE].textures.append({ nativeBinding, t });
} }
} }
break; break;
@ -928,19 +973,19 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD
id<MTLBuffer> mtlbuf = bufD->d->buf[0]; id<MTLBuffer> mtlbuf = bufD->d->buf[0];
quint32 offset = b->u.sbuf.offset; quint32 offset = b->u.sbuf.offset;
if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) { if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) {
const int nativeBinding = mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Buffer); const int nativeBinding = mapBinding(b->binding, QMetalShaderResourceBindingsData::VERTEX, nativeResourceBindingMaps, BindingType::Buffer);
if (nativeBinding >= 0) if (nativeBinding >= 0)
res[VERTEX].buffers.append({ nativeBinding, mtlbuf, offset }); bindingData.res[QMetalShaderResourceBindingsData::VERTEX].buffers.append({ nativeBinding, mtlbuf, offset });
} }
if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) { if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
const int nativeBinding = mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Buffer); const int nativeBinding = mapBinding(b->binding, QMetalShaderResourceBindingsData::FRAGMENT, nativeResourceBindingMaps, BindingType::Buffer);
if (nativeBinding >= 0) if (nativeBinding >= 0)
res[FRAGMENT].buffers.append({ nativeBinding, mtlbuf, offset }); bindingData.res[QMetalShaderResourceBindingsData::FRAGMENT].buffers.append({ nativeBinding, mtlbuf, offset });
} }
if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) { if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
const int nativeBinding = mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Buffer); const int nativeBinding = mapBinding(b->binding, QMetalShaderResourceBindingsData::COMPUTE, nativeResourceBindingMaps, BindingType::Buffer);
if (nativeBinding >= 0) if (nativeBinding >= 0)
res[COMPUTE].buffers.append({ nativeBinding, mtlbuf, offset }); bindingData.res[QMetalShaderResourceBindingsData::COMPUTE].buffers.append({ nativeBinding, mtlbuf, offset });
} }
} }
break; break;
@ -951,9 +996,9 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD
} }
for (int stage = 0; stage < SUPPORTED_STAGES; ++stage) { for (int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
if (cbD->recordingPass != QMetalCommandBuffer::RenderPass && (stage == VERTEX || stage == FRAGMENT)) if (cbD->recordingPass != QMetalCommandBuffer::RenderPass && (stage == QMetalShaderResourceBindingsData::VERTEX || stage == QMetalShaderResourceBindingsData::FRAGMENT))
continue; continue;
if (cbD->recordingPass != QMetalCommandBuffer::ComputePass && stage == COMPUTE) if (cbD->recordingPass != QMetalCommandBuffer::ComputePass && stage == QMetalShaderResourceBindingsData::COMPUTE)
continue; continue;
// QRhiBatchedBindings works with the native bindings and expects // QRhiBatchedBindings works with the native bindings and expects
@ -961,33 +1006,42 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD
// on the QRhi (SPIR-V) binding) is not helpful in this regard, so we // on the QRhi (SPIR-V) binding) is not helpful in this regard, so we
// have to sort here every time. // have to sort here every time.
std::sort(res[stage].buffers.begin(), res[stage].buffers.end(), [](const Stage::Buffer &a, const Stage::Buffer &b) { std::sort(bindingData.res[stage].buffers.begin(), bindingData.res[stage].buffers.end(), [](const QMetalShaderResourceBindingsData::Stage::Buffer &a, const QMetalShaderResourceBindingsData::Stage::Buffer &b) {
return a.nativeBinding < b.nativeBinding; return a.nativeBinding < b.nativeBinding;
}); });
for (const Stage::Buffer &buf : qAsConst(res[stage].buffers)) { for (const QMetalShaderResourceBindingsData::Stage::Buffer &buf : qAsConst(bindingData.res[stage].buffers)) {
res[stage].bufferBatches.feed(buf.nativeBinding, buf.mtlbuf); bindingData.res[stage].bufferBatches.feed(buf.nativeBinding, buf.mtlbuf);
res[stage].bufferOffsetBatches.feed(buf.nativeBinding, buf.offset); bindingData.res[stage].bufferOffsetBatches.feed(buf.nativeBinding, buf.offset);
} }
res[stage].bufferBatches.finish(); bindingData.res[stage].bufferBatches.finish();
res[stage].bufferOffsetBatches.finish(); bindingData.res[stage].bufferOffsetBatches.finish();
for (int i = 0, ie = res[stage].bufferBatches.batches.count(); i != ie; ++i) { for (int i = 0, ie = bindingData.res[stage].bufferBatches.batches.count(); i != ie; ++i) {
const auto &bufferBatch(res[stage].bufferBatches.batches[i]); const auto &bufferBatch(bindingData.res[stage].bufferBatches.batches[i]);
const auto &offsetBatch(res[stage].bufferOffsetBatches.batches[i]); const auto &offsetBatch(bindingData.res[stage].bufferOffsetBatches.batches[i]);
// skip setting Buffer binding if the current state is already correct
if (cbD->d->currentShaderResourceBindingState.res[stage].bufferBatches.batches.count() > i &&
cbD->d->currentShaderResourceBindingState.res[stage].bufferOffsetBatches.batches.count() > i &&
areBufferBatchesEqual(bufferBatch,
offsetBatch,
cbD->d->currentShaderResourceBindingState.res[stage].bufferBatches.batches[i],
cbD->d->currentShaderResourceBindingState.res[stage].bufferOffsetBatches.batches[i])) {
continue;
}
switch (stage) { switch (stage) {
case VERTEX: case QMetalShaderResourceBindingsData::VERTEX:
[cbD->d->currentRenderPassEncoder setVertexBuffers: bufferBatch.resources.constData() [cbD->d->currentRenderPassEncoder setVertexBuffers: bufferBatch.resources.constData()
offsets: offsetBatch.resources.constData() offsets: offsetBatch.resources.constData()
withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))]; withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
break; break;
case FRAGMENT: case QMetalShaderResourceBindingsData::FRAGMENT:
[cbD->d->currentRenderPassEncoder setFragmentBuffers: bufferBatch.resources.constData() [cbD->d->currentRenderPassEncoder setFragmentBuffers: bufferBatch.resources.constData()
offsets: offsetBatch.resources.constData() offsets: offsetBatch.resources.constData()
withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))]; withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
break; break;
case COMPUTE: case QMetalShaderResourceBindingsData::COMPUTE:
[cbD->d->currentComputePassEncoder setBuffers: bufferBatch.resources.constData() [cbD->d->currentComputePassEncoder setBuffers: bufferBatch.resources.constData()
offsets: offsetBatch.resources.constData() offsets: offsetBatch.resources.constData()
withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))]; withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
@ -1001,35 +1055,40 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD
if (offsetOnlyChange) if (offsetOnlyChange)
continue; continue;
std::sort(res[stage].textures.begin(), res[stage].textures.end(), [](const Stage::Texture &a, const Stage::Texture &b) { std::sort(bindingData.res[stage].textures.begin(), bindingData.res[stage].textures.end(), [](const QMetalShaderResourceBindingsData::Stage::Texture &a, const QMetalShaderResourceBindingsData::Stage::Texture &b) {
return a.nativeBinding < b.nativeBinding; return a.nativeBinding < b.nativeBinding;
}); });
std::sort(res[stage].samplers.begin(), res[stage].samplers.end(), [](const Stage::Sampler &a, const Stage::Sampler &b) { std::sort(bindingData.res[stage].samplers.begin(), bindingData.res[stage].samplers.end(), [](const QMetalShaderResourceBindingsData::Stage::Sampler &a, const QMetalShaderResourceBindingsData::Stage::Sampler &b) {
return a.nativeBinding < b.nativeBinding; return a.nativeBinding < b.nativeBinding;
}); });
for (const Stage::Texture &t : qAsConst(res[stage].textures)) for (const QMetalShaderResourceBindingsData::Stage::Texture &t : qAsConst(bindingData.res[stage].textures))
res[stage].textureBatches.feed(t.nativeBinding, t.mtltex); bindingData.res[stage].textureBatches.feed(t.nativeBinding, t.mtltex);
for (const Stage::Sampler &s : qAsConst(res[stage].samplers)) for (const QMetalShaderResourceBindingsData::Stage::Sampler &s : qAsConst(bindingData.res[stage].samplers))
res[stage].samplerBatches.feed(s.nativeBinding, s.mtlsampler); bindingData.res[stage].samplerBatches.feed(s.nativeBinding, s.mtlsampler);
res[stage].textureBatches.finish(); bindingData.res[stage].textureBatches.finish();
res[stage].samplerBatches.finish(); bindingData.res[stage].samplerBatches.finish();
for (int i = 0, ie = res[stage].textureBatches.batches.count(); i != ie; ++i) { for (int i = 0, ie = bindingData.res[stage].textureBatches.batches.count(); i != ie; ++i) {
const auto &batch(res[stage].textureBatches.batches[i]); const auto &batch(bindingData.res[stage].textureBatches.batches[i]);
// skip setting Texture binding if the current state is already correct
if (cbD->d->currentShaderResourceBindingState.res[stage].textureBatches.batches.count() > i &&
areTextureBatchesEqual(batch, cbD->d->currentShaderResourceBindingState.res[stage].textureBatches.batches[i])) {
continue;
}
switch (stage) { switch (stage) {
case VERTEX: case QMetalShaderResourceBindingsData::VERTEX:
[cbD->d->currentRenderPassEncoder setVertexTextures: batch.resources.constData() [cbD->d->currentRenderPassEncoder setVertexTextures: batch.resources.constData()
withRange: NSMakeRange(batch.startBinding, NSUInteger(batch.resources.count()))]; withRange: NSMakeRange(batch.startBinding, NSUInteger(batch.resources.count()))];
break; break;
case FRAGMENT: case QMetalShaderResourceBindingsData::FRAGMENT:
[cbD->d->currentRenderPassEncoder setFragmentTextures: batch.resources.constData() [cbD->d->currentRenderPassEncoder setFragmentTextures: batch.resources.constData()
withRange: NSMakeRange(batch.startBinding, NSUInteger(batch.resources.count()))]; withRange: NSMakeRange(batch.startBinding, NSUInteger(batch.resources.count()))];
break; break;
case COMPUTE: case QMetalShaderResourceBindingsData::COMPUTE:
[cbD->d->currentComputePassEncoder setTextures: batch.resources.constData() [cbD->d->currentComputePassEncoder setTextures: batch.resources.constData()
withRange: NSMakeRange(batch.startBinding, NSUInteger(batch.resources.count()))]; withRange: NSMakeRange(batch.startBinding, NSUInteger(batch.resources.count()))];
break; break;
@ -1038,18 +1097,24 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD
break; break;
} }
} }
for (int i = 0, ie = res[stage].samplerBatches.batches.count(); i != ie; ++i) {
const auto &batch(res[stage].samplerBatches.batches[i]); for (int i = 0, ie = bindingData.res[stage].samplerBatches.batches.count(); i != ie; ++i) {
const auto &batch(bindingData.res[stage].samplerBatches.batches[i]);
// skip setting Sampler State if the current state is already correct
if (cbD->d->currentShaderResourceBindingState.res[stage].samplerBatches.batches.count() > i &&
areSamplerBatchesEqual(batch, cbD->d->currentShaderResourceBindingState.res[stage].samplerBatches.batches[i])) {
continue;
}
switch (stage) { switch (stage) {
case VERTEX: case QMetalShaderResourceBindingsData::VERTEX:
[cbD->d->currentRenderPassEncoder setVertexSamplerStates: batch.resources.constData() [cbD->d->currentRenderPassEncoder setVertexSamplerStates: batch.resources.constData()
withRange: NSMakeRange(batch.startBinding, NSUInteger(batch.resources.count()))]; withRange: NSMakeRange(batch.startBinding, NSUInteger(batch.resources.count()))];
break; break;
case FRAGMENT: case QMetalShaderResourceBindingsData::FRAGMENT:
[cbD->d->currentRenderPassEncoder setFragmentSamplerStates: batch.resources.constData() [cbD->d->currentRenderPassEncoder setFragmentSamplerStates: batch.resources.constData()
withRange: NSMakeRange(batch.startBinding, NSUInteger(batch.resources.count()))]; withRange: NSMakeRange(batch.startBinding, NSUInteger(batch.resources.count()))];
break; break;
case COMPUTE: case QMetalShaderResourceBindingsData::COMPUTE:
[cbD->d->currentComputePassEncoder setSamplerStates: batch.resources.constData() [cbD->d->currentComputePassEncoder setSamplerStates: batch.resources.constData()
withRange: NSMakeRange(batch.startBinding, NSUInteger(batch.resources.count()))]; withRange: NSMakeRange(batch.startBinding, NSUInteger(batch.resources.count()))];
break; break;
@ -1059,6 +1124,7 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD
} }
} }
} }
cbD->d->currentShaderResourceBindingState = bindingData;
} }
void QRhiMetal::setGraphicsPipeline(QRhiCommandBuffer *cb, QRhiGraphicsPipeline *ps) void QRhiMetal::setGraphicsPipeline(QRhiCommandBuffer *cb, QRhiGraphicsPipeline *ps)
@ -4086,6 +4152,7 @@ void QMetalCommandBuffer::resetPerPassCachedState()
currentFrontFaceWinding = -1; currentFrontFaceWinding = -1;
currentDepthBiasValues = { 0.0f, 0.0f }; currentDepthBiasValues = { 0.0f, 0.0f };
d->currentShaderResourceBindingState = {};
d->currentDepthStencilState = nil; d->currentDepthStencilState = nil;
d->currentFirstVertexBinding = -1; d->currentFirstVertexBinding = -1;
d->currentVertexInputsBuffers.clear(); d->currentVertexInputsBuffers.clear();