rhi: d3d: Fix dynamic offsets with multiple buffers
Fixes: QTBUG-86821 Change-Id: I57f86bf0f7e95b92f5b2c5fee587112ecf0fc8e6 Reviewed-by: Andy Nichols <andy.nichols@qt.io>bb10
parent
9d55eee8da
commit
fe3a1617af
|
|
@ -1903,14 +1903,17 @@ void QRhiD3D11::updateShaderResourceBindings(QD3D11ShaderResourceBindings *srbD,
|
|||
const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[])
|
||||
{
|
||||
srbD->vsubufs.clear();
|
||||
srbD->vsubuforigbindings.clear();
|
||||
srbD->vsubufoffsets.clear();
|
||||
srbD->vsubufsizes.clear();
|
||||
|
||||
srbD->fsubufs.clear();
|
||||
srbD->fsubuforigbindings.clear();
|
||||
srbD->fsubufoffsets.clear();
|
||||
srbD->fsubufsizes.clear();
|
||||
|
||||
srbD->csubufs.clear();
|
||||
srbD->csubuforigbindings.clear();
|
||||
srbD->csubufoffsets.clear();
|
||||
srbD->csubufsizes.clear();
|
||||
|
||||
|
|
@ -1927,6 +1930,7 @@ void QRhiD3D11::updateShaderResourceBindings(QD3D11ShaderResourceBindings *srbD,
|
|||
|
||||
struct Stage {
|
||||
struct Buffer {
|
||||
int binding; // stored and sent along in XXorigbindings just for applyDynamicOffsets()
|
||||
int breg; // b0, b1, ...
|
||||
ID3D11Buffer *buffer;
|
||||
uint offsetInConstants;
|
||||
|
|
@ -1960,9 +1964,12 @@ void QRhiD3D11::updateShaderResourceBindings(QD3D11ShaderResourceBindings *srbD,
|
|||
Q_ASSERT(aligned(b->u.ubuf.offset, 256) == b->u.ubuf.offset);
|
||||
bd.ubuf.id = bufD->m_id;
|
||||
bd.ubuf.generation = bufD->generation;
|
||||
// dynamic ubuf offsets are not considered here, those are baked in
|
||||
// Dynamic ubuf offsets are not considered here, those are baked in
|
||||
// at a later stage, which is good as vsubufoffsets and friends are
|
||||
// per-srb, not per-setShaderResources call
|
||||
// per-srb, not per-setShaderResources call. Other backends (GL,
|
||||
// Metal) are different in this respect since those do not store
|
||||
// per-srb vsubufoffsets etc. data so life's a bit easier for them.
|
||||
// But here we have to defer baking in the dynamic offset.
|
||||
const uint offsetInConstants = uint(b->u.ubuf.offset) / 16;
|
||||
// size must be 16 mult. (in constants, i.e. multiple of 256 bytes).
|
||||
// We can round up if needed since the buffers's actual size
|
||||
|
|
@ -1971,17 +1978,17 @@ void QRhiD3D11::updateShaderResourceBindings(QD3D11ShaderResourceBindings *srbD,
|
|||
if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) {
|
||||
QPair<int, int> nativeBinding = mapBinding(b->binding, RBM_VERTEX, nativeResourceBindingMaps);
|
||||
if (nativeBinding.first >= 0)
|
||||
res[RBM_VERTEX].buffers.append({ nativeBinding.first, bufD->buffer, offsetInConstants, sizeInConstants });
|
||||
res[RBM_VERTEX].buffers.append({ b->binding, nativeBinding.first, bufD->buffer, offsetInConstants, sizeInConstants });
|
||||
}
|
||||
if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
|
||||
QPair<int, int> nativeBinding = mapBinding(b->binding, RBM_FRAGMENT, nativeResourceBindingMaps);
|
||||
if (nativeBinding.first >= 0)
|
||||
res[RBM_FRAGMENT].buffers.append({ nativeBinding.first, bufD->buffer, offsetInConstants, sizeInConstants });
|
||||
res[RBM_FRAGMENT].buffers.append({ b->binding, nativeBinding.first, bufD->buffer, offsetInConstants, sizeInConstants });
|
||||
}
|
||||
if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
|
||||
QPair<int, int> nativeBinding = mapBinding(b->binding, RBM_COMPUTE, nativeResourceBindingMaps);
|
||||
if (nativeBinding.first >= 0)
|
||||
res[RBM_COMPUTE].buffers.append({ nativeBinding.first, bufD->buffer, offsetInConstants, sizeInConstants });
|
||||
res[RBM_COMPUTE].buffers.append({ b->binding, nativeBinding.first, bufD->buffer, offsetInConstants, sizeInConstants });
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
@ -2088,28 +2095,34 @@ void QRhiD3D11::updateShaderResourceBindings(QD3D11ShaderResourceBindings *srbD,
|
|||
|
||||
for (const Stage::Buffer &buf : qAsConst(res[RBM_VERTEX].buffers)) {
|
||||
srbD->vsubufs.feed(buf.breg, buf.buffer);
|
||||
srbD->vsubuforigbindings.feed(buf.breg, UINT(buf.binding));
|
||||
srbD->vsubufoffsets.feed(buf.breg, buf.offsetInConstants);
|
||||
srbD->vsubufsizes.feed(buf.breg, buf.sizeInConstants);
|
||||
}
|
||||
srbD->vsubufs.finish();
|
||||
srbD->vsubuforigbindings.finish();
|
||||
srbD->vsubufoffsets.finish();
|
||||
srbD->vsubufsizes.finish();
|
||||
|
||||
for (const Stage::Buffer &buf : qAsConst(res[RBM_FRAGMENT].buffers)) {
|
||||
srbD->fsubufs.feed(buf.breg, buf.buffer);
|
||||
srbD->fsubuforigbindings.feed(buf.breg, UINT(buf.binding));
|
||||
srbD->fsubufoffsets.feed(buf.breg, buf.offsetInConstants);
|
||||
srbD->fsubufsizes.feed(buf.breg, buf.sizeInConstants);
|
||||
}
|
||||
srbD->fsubufs.finish();
|
||||
srbD->fsubuforigbindings.finish();
|
||||
srbD->fsubufoffsets.finish();
|
||||
srbD->fsubufsizes.finish();
|
||||
|
||||
for (const Stage::Buffer &buf : qAsConst(res[RBM_COMPUTE].buffers)) {
|
||||
srbD->csubufs.feed(buf.breg, buf.buffer);
|
||||
srbD->csubuforigbindings.feed(buf.breg, UINT(buf.binding));
|
||||
srbD->csubufoffsets.feed(buf.breg, buf.offsetInConstants);
|
||||
srbD->csubufsizes.feed(buf.breg, buf.sizeInConstants);
|
||||
}
|
||||
srbD->csubufs.finish();
|
||||
srbD->csubuforigbindings.finish();
|
||||
srbD->csubufoffsets.finish();
|
||||
srbD->csubufsizes.finish();
|
||||
|
||||
|
|
@ -2158,17 +2171,20 @@ void QRhiD3D11::executeBufferHostWrites(QD3D11Buffer *bufD)
|
|||
|
||||
static void applyDynamicOffsets(QVarLengthArray<UINT, 4> *offsets,
|
||||
int batchIndex,
|
||||
QRhiBatchedBindings<ID3D11Buffer *> *ubufs,
|
||||
QRhiBatchedBindings<UINT> *ubufoffsets,
|
||||
QRhiBatchedBindings<UINT> *originalBindings,
|
||||
QRhiBatchedBindings<UINT> *staticOffsets,
|
||||
const uint *dynOfsPairs, int dynOfsPairCount)
|
||||
{
|
||||
const int count = ubufs->batches[batchIndex].resources.count();
|
||||
const UINT startBinding = ubufs->batches[batchIndex].startBinding;
|
||||
*offsets = ubufoffsets->batches[batchIndex].resources;
|
||||
const int count = staticOffsets->batches[batchIndex].resources.count();
|
||||
// Make a copy of the offset list, the entries that have no corresponding
|
||||
// dynamic offset will continue to use the existing offset value.
|
||||
*offsets = staticOffsets->batches[batchIndex].resources;
|
||||
for (int b = 0; b < count; ++b) {
|
||||
for (int di = 0; di < dynOfsPairCount; ++di) {
|
||||
const uint binding = dynOfsPairs[2 * di];
|
||||
if (binding == startBinding + UINT(b)) {
|
||||
// binding is the SPIR-V style binding point here, nothing to do
|
||||
// with the native one.
|
||||
if (binding == originalBindings->batches[batchIndex].resources[b]) {
|
||||
const uint offsetInConstants = dynOfsPairs[2 * di + 1];
|
||||
(*offsets)[b] = offsetInConstants;
|
||||
break;
|
||||
|
|
@ -2258,7 +2274,8 @@ void QRhiD3D11::bindShaderResources(QD3D11ShaderResourceBindings *srbD,
|
|||
srbD->vsubufsizes.batches[i].resources.constData());
|
||||
} else {
|
||||
QVarLengthArray<UINT, 4> offsets;
|
||||
applyDynamicOffsets(&offsets, i, &srbD->vsubufs, &srbD->vsubufoffsets, dynOfsPairs, dynOfsPairCount);
|
||||
applyDynamicOffsets(&offsets, i, &srbD->vsubuforigbindings, &srbD->vsubufoffsets,
|
||||
dynOfsPairs, dynOfsPairCount);
|
||||
context->VSSetConstantBuffers1(srbD->vsubufs.batches[i].startBinding,
|
||||
count,
|
||||
srbD->vsubufs.batches[i].resources.constData(),
|
||||
|
|
@ -2282,7 +2299,8 @@ void QRhiD3D11::bindShaderResources(QD3D11ShaderResourceBindings *srbD,
|
|||
srbD->fsubufsizes.batches[i].resources.constData());
|
||||
} else {
|
||||
QVarLengthArray<UINT, 4> offsets;
|
||||
applyDynamicOffsets(&offsets, i, &srbD->fsubufs, &srbD->fsubufoffsets, dynOfsPairs, dynOfsPairCount);
|
||||
applyDynamicOffsets(&offsets, i, &srbD->fsubuforigbindings, &srbD->fsubufoffsets,
|
||||
dynOfsPairs, dynOfsPairCount);
|
||||
context->PSSetConstantBuffers1(srbD->fsubufs.batches[i].startBinding,
|
||||
count,
|
||||
srbD->fsubufs.batches[i].resources.constData(),
|
||||
|
|
@ -2306,7 +2324,8 @@ void QRhiD3D11::bindShaderResources(QD3D11ShaderResourceBindings *srbD,
|
|||
srbD->csubufsizes.batches[i].resources.constData());
|
||||
} else {
|
||||
QVarLengthArray<UINT, 4> offsets;
|
||||
applyDynamicOffsets(&offsets, i, &srbD->csubufs, &srbD->csubufoffsets, dynOfsPairs, dynOfsPairCount);
|
||||
applyDynamicOffsets(&offsets, i, &srbD->csubuforigbindings, &srbD->csubufoffsets,
|
||||
dynOfsPairs, dynOfsPairCount);
|
||||
context->CSSetConstantBuffers1(srbD->csubufs.batches[i].startBinding,
|
||||
count,
|
||||
srbD->csubufs.batches[i].resources.constData(),
|
||||
|
|
|
|||
|
|
@ -238,14 +238,17 @@ struct QD3D11ShaderResourceBindings : public QRhiShaderResourceBindings
|
|||
QVarLengthArray<BoundResourceData, 8> boundResourceData;
|
||||
|
||||
QRhiBatchedBindings<ID3D11Buffer *> vsubufs;
|
||||
QRhiBatchedBindings<UINT> vsubuforigbindings;
|
||||
QRhiBatchedBindings<UINT> vsubufoffsets;
|
||||
QRhiBatchedBindings<UINT> vsubufsizes;
|
||||
|
||||
QRhiBatchedBindings<ID3D11Buffer *> fsubufs;
|
||||
QRhiBatchedBindings<UINT> fsubuforigbindings;
|
||||
QRhiBatchedBindings<UINT> fsubufoffsets;
|
||||
QRhiBatchedBindings<UINT> fsubufsizes;
|
||||
|
||||
QRhiBatchedBindings<ID3D11Buffer *> csubufs;
|
||||
QRhiBatchedBindings<UINT> csubuforigbindings;
|
||||
QRhiBatchedBindings<UINT> csubufoffsets;
|
||||
QRhiBatchedBindings<UINT> csubufsizes;
|
||||
|
||||
|
|
|
|||
|
|
@ -390,7 +390,7 @@ struct QGles2CommandBuffer : public QRhiCommandBuffer
|
|||
QRhiComputePipeline *maybeComputePs;
|
||||
QRhiShaderResourceBindings *srb;
|
||||
int dynamicOffsetCount;
|
||||
uint dynamicOffsetPairs[MAX_UBUF_BINDINGS * 2]; // binding, offsetInConstants
|
||||
uint dynamicOffsetPairs[MAX_UBUF_BINDINGS * 2]; // binding, offset
|
||||
} bindShaderResources;
|
||||
struct {
|
||||
GLbitfield mask;
|
||||
|
|
|
|||
|
|
@ -44,3 +44,5 @@ qsb --glsl "150,120,100 es" --hlsl 50 -c --msl 12 -o simpletextured.frag.qsb sim
|
|||
qsb --glsl "150,120,100 es" --hlsl 50 -c --msl 20 -o simpletextured_array.frag.qsb simpletextured_array.frag
|
||||
qsb --glsl "150,120,100 es" --hlsl 50 -c --msl 12 -o textured.vert.qsb textured.vert
|
||||
qsb --glsl "150,120,100 es" --hlsl 50 -c --msl 12 -o textured.frag.qsb textured.frag
|
||||
qsb --glsl "150,120,100 es" --hlsl 50 -c --msl 12 -o textured_multiubuf.vert.qsb textured_multiubuf.vert
|
||||
qsb --glsl "150,120,100 es" --hlsl 50 -c --msl 12 -o textured_multiubuf.frag.qsb textured_multiubuf.frag
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,18 @@
|
|||
#version 440
|
||||
|
||||
layout(location = 0) in vec2 uv;
|
||||
layout(location = 0) out vec4 fragColor;
|
||||
|
||||
layout(std140, binding = 1) uniform buf {
|
||||
float opacity;
|
||||
} fubuf;
|
||||
|
||||
layout(binding = 2) uniform sampler2D tex;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 c = texture(tex, uv);
|
||||
c.a *= fubuf.opacity;
|
||||
c.rgb *= c.a;
|
||||
fragColor = c;
|
||||
}
|
||||
Binary file not shown.
|
|
@ -0,0 +1,18 @@
|
|||
#version 440
|
||||
|
||||
layout(location = 0) in vec4 position;
|
||||
layout(location = 1) in vec2 texcoord;
|
||||
|
||||
layout(location = 0) out vec2 uv;
|
||||
|
||||
layout(std140, binding = 0) uniform buf {
|
||||
mat4 matrix;
|
||||
} vubuf;
|
||||
|
||||
out gl_PerVertex { vec4 gl_Position; };
|
||||
|
||||
void main()
|
||||
{
|
||||
uv = texcoord;
|
||||
gl_Position = vubuf.matrix * position;
|
||||
}
|
||||
Binary file not shown.
|
|
@ -112,6 +112,8 @@ private slots:
|
|||
void renderToTextureTexturedQuadAndUniformBuffer();
|
||||
void renderToTextureDeferredSrb_data();
|
||||
void renderToTextureDeferredSrb();
|
||||
void renderToTextureMultipleUniformBuffersAndDynamicOffset_data();
|
||||
void renderToTextureMultipleUniformBuffersAndDynamicOffset();
|
||||
void renderToWindowSimple_data();
|
||||
void renderToWindowSimple();
|
||||
void finishWithinSwapchainFrame_data();
|
||||
|
|
@ -2354,6 +2356,175 @@ void tst_QRhi::renderToTextureDeferredSrb()
|
|||
QCOMPARE(result.pixel(4, 227), empty);
|
||||
}
|
||||
|
||||
void tst_QRhi::renderToTextureMultipleUniformBuffersAndDynamicOffset_data()
|
||||
{
|
||||
rhiTestData();
|
||||
}
|
||||
|
||||
void tst_QRhi::renderToTextureMultipleUniformBuffersAndDynamicOffset()
|
||||
{
|
||||
QFETCH(QRhi::Implementation, impl);
|
||||
QFETCH(QRhiInitParams *, initParams);
|
||||
|
||||
QScopedPointer<QRhi> rhi(QRhi::create(impl, initParams, QRhi::Flags(), nullptr));
|
||||
if (!rhi)
|
||||
QSKIP("QRhi could not be created, skipping testing rendering");
|
||||
|
||||
QImage inputImage;
|
||||
inputImage.load(QLatin1String(":/data/qt256.png"));
|
||||
QVERIFY(!inputImage.isNull());
|
||||
|
||||
QScopedPointer<QRhiTexture> texture(rhi->newTexture(QRhiTexture::RGBA8, inputImage.size(), 1,
|
||||
QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource));
|
||||
QVERIFY(texture->create());
|
||||
|
||||
QScopedPointer<QRhiTextureRenderTarget> rt(rhi->newTextureRenderTarget({ texture.data() }));
|
||||
QScopedPointer<QRhiRenderPassDescriptor> rpDesc(rt->newCompatibleRenderPassDescriptor());
|
||||
rt->setRenderPassDescriptor(rpDesc.data());
|
||||
QVERIFY(rt->create());
|
||||
|
||||
QRhiCommandBuffer *cb = nullptr;
|
||||
QVERIFY(rhi->beginOffscreenFrame(&cb) == QRhi::FrameOpSuccess);
|
||||
QVERIFY(cb);
|
||||
|
||||
QRhiResourceUpdateBatch *updates = rhi->nextResourceUpdateBatch();
|
||||
|
||||
static const float verticesUvs[] = {
|
||||
-1.0f, -1.0f, 0.0f, 0.0f,
|
||||
1.0f, -1.0f, 1.0f, 0.0f,
|
||||
-1.0f, 1.0f, 0.0f, 1.0f,
|
||||
1.0f, 1.0f, 1.0f, 1.0f
|
||||
};
|
||||
QScopedPointer<QRhiBuffer> vbuf(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(verticesUvs)));
|
||||
QVERIFY(vbuf->create());
|
||||
updates->uploadStaticBuffer(vbuf.data(), verticesUvs);
|
||||
|
||||
QScopedPointer<QRhiTexture> inputTexture(rhi->newTexture(QRhiTexture::RGBA8, inputImage.size()));
|
||||
QVERIFY(inputTexture->create());
|
||||
updates->uploadTexture(inputTexture.data(), inputImage);
|
||||
|
||||
QScopedPointer<QRhiSampler> sampler(rhi->newSampler(QRhiSampler::Nearest, QRhiSampler::Nearest, QRhiSampler::None,
|
||||
QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge));
|
||||
QVERIFY(sampler->create());
|
||||
|
||||
const int MATRIX_COUNT = 4; // put 4 mat4s into the buffer, will only use one
|
||||
const int ubufElemSize = rhi->ubufAligned(64);
|
||||
QVERIFY(ubufElemSize >= 64);
|
||||
QScopedPointer<QRhiBuffer> ubuf(rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, MATRIX_COUNT * ubufElemSize));
|
||||
QVERIFY(ubuf->create());
|
||||
|
||||
float zeroes[16];
|
||||
memset(zeroes, 0, sizeof(zeroes));
|
||||
updates->updateDynamicBuffer(ubuf.data(), 0, 64, zeroes);
|
||||
updates->updateDynamicBuffer(ubuf.data(), ubufElemSize, 64, zeroes);
|
||||
// the only correct matrix is the third one
|
||||
QMatrix4x4 matrix;
|
||||
updates->updateDynamicBuffer(ubuf.data(), ubufElemSize * 2, 64, matrix.constData());
|
||||
updates->updateDynamicBuffer(ubuf.data(), ubufElemSize * 3, 64, zeroes);
|
||||
|
||||
const int OPACITY_COUNT = 6; // put 6 floats into the buffer, will only use one
|
||||
const int ubuf2ElemSize = rhi->ubufAligned(4);
|
||||
QVERIFY(ubuf2ElemSize >= 4);
|
||||
QScopedPointer<QRhiBuffer> ubuf2(rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, OPACITY_COUNT * ubuf2ElemSize));
|
||||
QVERIFY(ubuf2->create());
|
||||
|
||||
updates->updateDynamicBuffer(ubuf2.data(), 0, 4, &zeroes[0]);
|
||||
updates->updateDynamicBuffer(ubuf2.data(), ubuf2ElemSize, 4, &zeroes[0]);
|
||||
updates->updateDynamicBuffer(ubuf2.data(), ubuf2ElemSize * 2, 4, &zeroes[0]);
|
||||
// the only correct opacity value is the fourth one
|
||||
float opacity = 0.5f;
|
||||
updates->updateDynamicBuffer(ubuf2.data(), ubuf2ElemSize * 3, 4, &opacity);
|
||||
updates->updateDynamicBuffer(ubuf2.data(), ubuf2ElemSize * 4, 4, &zeroes[0]);
|
||||
updates->updateDynamicBuffer(ubuf2.data(), ubuf2ElemSize * 5, 4, &zeroes[0]);
|
||||
|
||||
const QRhiShaderResourceBinding::StageFlags commonVisibility = QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage;
|
||||
QScopedPointer<QRhiShaderResourceBindings> srb(rhi->newShaderResourceBindings());
|
||||
srb->setBindings({
|
||||
QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0, commonVisibility, ubuf.data(), 64),
|
||||
QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(1, commonVisibility, ubuf2.data(), 4),
|
||||
QRhiShaderResourceBinding::sampledTexture(2, QRhiShaderResourceBinding::FragmentStage, inputTexture.data(), sampler.data())
|
||||
});
|
||||
QVERIFY(srb->create());
|
||||
|
||||
QScopedPointer<QRhiGraphicsPipeline> pipeline(rhi->newGraphicsPipeline());
|
||||
pipeline->setTopology(QRhiGraphicsPipeline::TriangleStrip);
|
||||
QShader vs = loadShader(":/data/textured_multiubuf.vert.qsb");
|
||||
QVERIFY(vs.isValid());
|
||||
QShader fs = loadShader(":/data/textured_multiubuf.frag.qsb");
|
||||
QVERIFY(fs.isValid());
|
||||
pipeline->setShaderStages({ { QRhiShaderStage::Vertex, vs }, { QRhiShaderStage::Fragment, fs } });
|
||||
QRhiVertexInputLayout inputLayout;
|
||||
inputLayout.setBindings({ { 4 * sizeof(float) } });
|
||||
inputLayout.setAttributes({
|
||||
{ 0, 0, QRhiVertexInputAttribute::Float2, 0 },
|
||||
{ 0, 1, QRhiVertexInputAttribute::Float2, 2 * sizeof(float) }
|
||||
});
|
||||
pipeline->setVertexInputLayout(inputLayout);
|
||||
pipeline->setShaderResourceBindings(srb.data());
|
||||
pipeline->setRenderPassDescriptor(rpDesc.data());
|
||||
|
||||
QVERIFY(pipeline->create());
|
||||
|
||||
cb->beginPass(rt.data(), Qt::black, { 1.0f, 0 }, updates);
|
||||
cb->setGraphicsPipeline(pipeline.data());
|
||||
|
||||
// Now the magic, expose the 3rd matrix and 4th opacity value to the shader.
|
||||
// If the handling of dynamic offsets were broken, the shaders would likely
|
||||
// "see" an all zero matrix and zero opacity, thus leading to different
|
||||
// rendering output. This way we can verify if using dynamic offsets, and
|
||||
// more than one at the same time, is functional.
|
||||
QVarLengthArray<QPair<int, quint32>, 2> dynamicOffset = {
|
||||
{ 0, quint32(ubufElemSize * 2) },
|
||||
{ 1, quint32(ubuf2ElemSize * 3) },
|
||||
};
|
||||
cb->setShaderResources(srb.data(), 2, dynamicOffset.constData());
|
||||
|
||||
cb->setViewport({ 0, 0, float(texture->pixelSize().width()), float(texture->pixelSize().height()) });
|
||||
QRhiCommandBuffer::VertexInput vbindings(vbuf.data(), 0);
|
||||
cb->setVertexInput(0, 1, &vbindings);
|
||||
cb->draw(4);
|
||||
|
||||
QRhiReadbackResult readResult;
|
||||
QImage result;
|
||||
readResult.completed = [&readResult, &result] {
|
||||
result = QImage(reinterpret_cast<const uchar *>(readResult.data.constData()),
|
||||
readResult.pixelSize.width(), readResult.pixelSize.height(),
|
||||
QImage::Format_RGBA8888_Premultiplied);
|
||||
};
|
||||
QRhiResourceUpdateBatch *readbackBatch = rhi->nextResourceUpdateBatch();
|
||||
readbackBatch->readBackTexture({ texture.data() }, &readResult);
|
||||
cb->endPass(readbackBatch);
|
||||
|
||||
rhi->endOffscreenFrame();
|
||||
|
||||
QVERIFY(!result.isNull());
|
||||
|
||||
if (impl == QRhi::Null)
|
||||
return;
|
||||
|
||||
if (rhi->isYUpInFramebuffer() != rhi->isYUpInNDC())
|
||||
result = std::move(result).mirrored();
|
||||
|
||||
// opacity 0.5 (premultiplied)
|
||||
static const auto checkSemiWhite = [](const QRgb &c) {
|
||||
QRgb semiWhite127 = qPremultiply(qRgba(255, 255, 255, 127));
|
||||
QRgb semiWhite128 = qPremultiply(qRgba(255, 255, 255, 128));
|
||||
return c == semiWhite127 || c == semiWhite128;
|
||||
};
|
||||
QVERIFY(checkSemiWhite(result.pixel(79, 77)));
|
||||
QVERIFY(checkSemiWhite(result.pixel(124, 81)));
|
||||
QVERIFY(checkSemiWhite(result.pixel(128, 149)));
|
||||
QVERIFY(checkSemiWhite(result.pixel(120, 189)));
|
||||
QVERIFY(checkSemiWhite(result.pixel(116, 185)));
|
||||
QVERIFY(checkSemiWhite(result.pixel(191, 172)));
|
||||
|
||||
QRgb empty = qRgba(0, 0, 0, 0);
|
||||
QCOMPARE(result.pixel(11, 45), empty);
|
||||
QCOMPARE(result.pixel(246, 202), empty);
|
||||
QCOMPARE(result.pixel(130, 18), empty);
|
||||
QCOMPARE(result.pixel(4, 227), empty);
|
||||
}
|
||||
|
||||
void tst_QRhi::setWindowType(QWindow *window, QRhi::Implementation impl)
|
||||
{
|
||||
switch (impl) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue