rhi: metal: Make sure the resources are sorted based on the native bindings
...before generating batches for the encoder's set* methods. Otherwise there is a chance we end up in an assertion in case the native binding number for a buffer/texture/sampler happens to be smaller than the native binding of the previous. (we pre-sort based on the SPIR-V binding but that is not what the Metal API works with in the end) Task-number: QTBUG-81822 Change-Id: Iddfed168e065e3c7f6a09ad6dd4efdafa891b339 Reviewed-by: Eirik Aavitsland <eirik.aavitsland@qt.io>
This commit is contained in:
parent
26f6aa3e50
commit
1d77945094
@ -684,11 +684,27 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD
|
|||||||
bool offsetOnlyChange,
|
bool offsetOnlyChange,
|
||||||
const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[SUPPORTED_STAGES])
|
const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[SUPPORTED_STAGES])
|
||||||
{
|
{
|
||||||
struct {
|
struct Stage {
|
||||||
QRhiBatchedBindings<id<MTLBuffer> > buffers;
|
struct Buffer {
|
||||||
QRhiBatchedBindings<NSUInteger> bufferOffsets;
|
int nativeBinding;
|
||||||
QRhiBatchedBindings<id<MTLTexture> > textures;
|
id<MTLBuffer> mtlbuf;
|
||||||
QRhiBatchedBindings<id<MTLSamplerState> > samplers;
|
uint 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];
|
} res[SUPPORTED_STAGES];
|
||||||
enum { VERTEX = 0, FRAGMENT = 1, COMPUTE = 2 };
|
enum { VERTEX = 0, FRAGMENT = 1, COMPUTE = 2 };
|
||||||
|
|
||||||
@ -709,24 +725,18 @@ 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, VERTEX, nativeResourceBindingMaps, BindingType::Buffer);
|
||||||
if (nativeBinding >= 0) {
|
if (nativeBinding >= 0)
|
||||||
res[VERTEX].buffers.feed(nativeBinding, mtlbuf);
|
res[VERTEX].buffers.append({ nativeBinding, mtlbuf, offset });
|
||||||
res[VERTEX].bufferOffsets.feed(b->binding, 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, FRAGMENT, nativeResourceBindingMaps, BindingType::Buffer);
|
||||||
if (nativeBinding >= 0) {
|
if (nativeBinding >= 0)
|
||||||
res[FRAGMENT].buffers.feed(nativeBinding, mtlbuf);
|
res[FRAGMENT].buffers.append({ nativeBinding, mtlbuf, offset });
|
||||||
res[FRAGMENT].bufferOffsets.feed(b->binding, 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, COMPUTE, nativeResourceBindingMaps, BindingType::Buffer);
|
||||||
if (nativeBinding >= 0) {
|
if (nativeBinding >= 0)
|
||||||
res[COMPUTE].buffers.feed(nativeBinding, mtlbuf);
|
res[COMPUTE].buffers.append({ nativeBinding, mtlbuf, offset });
|
||||||
res[COMPUTE].bufferOffsets.feed(b->binding, offset);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -738,24 +748,24 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD
|
|||||||
const int nativeBindingTexture = mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Texture);
|
const int nativeBindingTexture = mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Texture);
|
||||||
const int nativeBindingSampler = mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Sampler);
|
const int nativeBindingSampler = mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Sampler);
|
||||||
if (nativeBindingTexture >= 0 && nativeBindingSampler >= 0) {
|
if (nativeBindingTexture >= 0 && nativeBindingSampler >= 0) {
|
||||||
res[VERTEX].textures.feed(nativeBindingTexture, texD->d->tex);
|
res[VERTEX].textures.append({ nativeBindingTexture, texD->d->tex });
|
||||||
res[VERTEX].samplers.feed(nativeBindingSampler, samplerD->d->samplerState);
|
res[VERTEX].samplers.append({ nativeBindingSampler, samplerD->d->samplerState });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
|
if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
|
||||||
const int nativeBindingTexture = mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Texture);
|
const int nativeBindingTexture = mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Texture);
|
||||||
const int nativeBindingSampler = mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Sampler);
|
const int nativeBindingSampler = mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Sampler);
|
||||||
if (nativeBindingTexture >= 0 && nativeBindingSampler >= 0) {
|
if (nativeBindingTexture >= 0 && nativeBindingSampler >= 0) {
|
||||||
res[FRAGMENT].textures.feed(nativeBindingTexture, texD->d->tex);
|
res[FRAGMENT].textures.append({ nativeBindingTexture, texD->d->tex });
|
||||||
res[FRAGMENT].samplers.feed(nativeBindingSampler, samplerD->d->samplerState);
|
res[FRAGMENT].samplers.append({ nativeBindingSampler, samplerD->d->samplerState });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
|
if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
|
||||||
const int nativeBindingTexture = mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Texture);
|
const int nativeBindingTexture = mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Texture);
|
||||||
const int nativeBindingSampler = mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Sampler);
|
const int nativeBindingSampler = mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Sampler);
|
||||||
if (nativeBindingTexture >= 0 && nativeBindingSampler >= 0) {
|
if (nativeBindingTexture >= 0 && nativeBindingSampler >= 0) {
|
||||||
res[COMPUTE].textures.feed(nativeBindingTexture, texD->d->tex);
|
res[COMPUTE].textures.append({ nativeBindingTexture, texD->d->tex });
|
||||||
res[COMPUTE].samplers.feed(nativeBindingSampler, samplerD->d->samplerState);
|
res[COMPUTE].samplers.append({ nativeBindingSampler, samplerD->d->samplerState });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -769,17 +779,17 @@ 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::Texture);
|
const int nativeBinding = mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Texture);
|
||||||
if (nativeBinding >= 0)
|
if (nativeBinding >= 0)
|
||||||
res[VERTEX].textures.feed(nativeBinding, t);
|
res[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, FRAGMENT, nativeResourceBindingMaps, BindingType::Texture);
|
||||||
if (nativeBinding >= 0)
|
if (nativeBinding >= 0)
|
||||||
res[FRAGMENT].textures.feed(nativeBinding, t);
|
res[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, COMPUTE, nativeResourceBindingMaps, BindingType::Texture);
|
||||||
if (nativeBinding >= 0)
|
if (nativeBinding >= 0)
|
||||||
res[COMPUTE].textures.feed(nativeBinding, t);
|
res[COMPUTE].textures.append({ nativeBinding, t });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -792,24 +802,18 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD
|
|||||||
uint offset = uint(b->u.sbuf.offset);
|
uint offset = uint(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, VERTEX, nativeResourceBindingMaps, BindingType::Buffer);
|
||||||
if (nativeBinding >= 0) {
|
if (nativeBinding >= 0)
|
||||||
res[VERTEX].buffers.feed(nativeBinding, mtlbuf);
|
res[VERTEX].buffers.append({ nativeBinding, mtlbuf, offset });
|
||||||
res[VERTEX].bufferOffsets.feed(b->binding, 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, FRAGMENT, nativeResourceBindingMaps, BindingType::Buffer);
|
||||||
if (nativeBinding >= 0) {
|
if (nativeBinding >= 0)
|
||||||
res[FRAGMENT].buffers.feed(nativeBinding, mtlbuf);
|
res[FRAGMENT].buffers.append({ nativeBinding, mtlbuf, offset });
|
||||||
res[FRAGMENT].bufferOffsets.feed(b->binding, 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, COMPUTE, nativeResourceBindingMaps, BindingType::Buffer);
|
||||||
if (nativeBinding >= 0) {
|
if (nativeBinding >= 0)
|
||||||
res[COMPUTE].buffers.feed(nativeBinding, mtlbuf);
|
res[COMPUTE].buffers.append({ nativeBinding, mtlbuf, offset });
|
||||||
res[COMPUTE].bufferOffsets.feed(b->binding, offset);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -825,12 +829,26 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD
|
|||||||
if (cbD->recordingPass != QMetalCommandBuffer::ComputePass && stage == COMPUTE)
|
if (cbD->recordingPass != QMetalCommandBuffer::ComputePass && stage == COMPUTE)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
res[stage].buffers.finish();
|
// QRhiBatchedBindings works with the native bindings and expects
|
||||||
res[stage].bufferOffsets.finish();
|
// sorted input. The pre-sorted QRhiShaderResourceBinding list (based
|
||||||
|
// on the QRhi (SPIR-V) binding) is not helpful in this regard, so we
|
||||||
|
// have to sort here every time.
|
||||||
|
|
||||||
for (int i = 0, ie = res[stage].buffers.batches.count(); i != ie; ++i) {
|
std::sort(res[stage].buffers.begin(), res[stage].buffers.end(), [](const Stage::Buffer &a, const Stage::Buffer &b) {
|
||||||
const auto &bufferBatch(res[stage].buffers.batches[i]);
|
return a.nativeBinding < b.nativeBinding;
|
||||||
const auto &offsetBatch(res[stage].bufferOffsets.batches[i]);
|
});
|
||||||
|
|
||||||
|
for (const Stage::Buffer &buf : qAsConst(res[stage].buffers)) {
|
||||||
|
res[stage].bufferBatches.feed(buf.nativeBinding, buf.mtlbuf);
|
||||||
|
res[stage].bufferOffsetBatches.feed(buf.nativeBinding, buf.offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
res[stage].bufferBatches.finish();
|
||||||
|
res[stage].bufferOffsetBatches.finish();
|
||||||
|
|
||||||
|
for (int i = 0, ie = res[stage].bufferBatches.batches.count(); i != ie; ++i) {
|
||||||
|
const auto &bufferBatch(res[stage].bufferBatches.batches[i]);
|
||||||
|
const auto &offsetBatch(res[stage].bufferOffsetBatches.batches[i]);
|
||||||
switch (stage) {
|
switch (stage) {
|
||||||
case VERTEX:
|
case VERTEX:
|
||||||
[cbD->d->currentRenderPassEncoder setVertexBuffers: bufferBatch.resources.constData()
|
[cbD->d->currentRenderPassEncoder setVertexBuffers: bufferBatch.resources.constData()
|
||||||
@ -856,11 +874,25 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD
|
|||||||
if (offsetOnlyChange)
|
if (offsetOnlyChange)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
res[stage].textures.finish();
|
std::sort(res[stage].textures.begin(), res[stage].textures.end(), [](const Stage::Texture &a, const Stage::Texture &b) {
|
||||||
res[stage].samplers.finish();
|
return a.nativeBinding < b.nativeBinding;
|
||||||
|
});
|
||||||
|
|
||||||
for (int i = 0, ie = res[stage].textures.batches.count(); i != ie; ++i) {
|
std::sort(res[stage].samplers.begin(), res[stage].samplers.end(), [](const Stage::Sampler &a, const Stage::Sampler &b) {
|
||||||
const auto &batch(res[stage].textures.batches[i]);
|
return a.nativeBinding < b.nativeBinding;
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const Stage::Texture &t : qAsConst(res[stage].textures))
|
||||||
|
res[stage].textureBatches.feed(t.nativeBinding, t.mtltex);
|
||||||
|
|
||||||
|
for (const Stage::Sampler &s : qAsConst(res[stage].samplers))
|
||||||
|
res[stage].samplerBatches.feed(s.nativeBinding, s.mtlsampler);
|
||||||
|
|
||||||
|
res[stage].textureBatches.finish();
|
||||||
|
res[stage].samplerBatches.finish();
|
||||||
|
|
||||||
|
for (int i = 0, ie = res[stage].textureBatches.batches.count(); i != ie; ++i) {
|
||||||
|
const auto &batch(res[stage].textureBatches.batches[i]);
|
||||||
switch (stage) {
|
switch (stage) {
|
||||||
case VERTEX:
|
case VERTEX:
|
||||||
[cbD->d->currentRenderPassEncoder setVertexTextures: batch.resources.constData()
|
[cbD->d->currentRenderPassEncoder setVertexTextures: batch.resources.constData()
|
||||||
@ -879,8 +911,8 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (int i = 0, ie = res[stage].samplers.batches.count(); i != ie; ++i) {
|
for (int i = 0, ie = res[stage].samplerBatches.batches.count(); i != ie; ++i) {
|
||||||
const auto &batch(res[stage].samplers.batches[i]);
|
const auto &batch(res[stage].samplerBatches.batches[i]);
|
||||||
switch (stage) {
|
switch (stage) {
|
||||||
case VERTEX:
|
case VERTEX:
|
||||||
[cbD->d->currentRenderPassEncoder setVertexSamplerStates: batch.resources.constData()
|
[cbD->d->currentRenderPassEncoder setVertexSamplerStates: batch.resources.constData()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user