rhi: Allow null resources in srb

In this case the srb represents the layout only, and can still be used
to create a pipeline. For setShaderResources() one will then need to use
another, layout compatible, srb that references valid resources.

Change-Id: I3ea5b63df3be8847540ca4c0c40fbd29dbed8fb7
Reviewed-by: Paul Lemire <paul.lemire@kdab.com>
Reviewed-by: Andy Nichols <andy.nichols@qt.io>
This commit is contained in:
Laszlo Agocs 2020-07-10 14:50:55 +02:00
parent e8d5000026
commit 351d42175d
4 changed files with 330 additions and 68 deletions

View File

@ -2854,7 +2854,15 @@ bool QRhiShaderResourceBinding::isLayoutCompatible(const QRhiShaderResourceBindi
\return a shader resource binding for the given binding number, pipeline
stages, and buffer specified by \a binding, \a stage, and \a buf.
\note \a buf must have been created with QRhiBuffer::UniformBuffer.
\note When \a buf is not null, it must have been created with
QRhiBuffer::UniformBuffer.
\note \a buf can be null. It is valid to create a
QRhiShaderResourceBindings with unspecified resources, but such an object
cannot be used with QRhiCommandBuffer::setShaderResources(). It is however
suitable for creating pipelines. Such a pipeline must then always be used
together with another, layout compatible QRhiShaderResourceBindings with
resources present passed to QRhiCommandBuffer::setShaderResources().
*/
QRhiShaderResourceBinding QRhiShaderResourceBinding::uniformBuffer(
int binding, StageFlags stage, QRhiBuffer *buf)
@ -2880,7 +2888,15 @@ QRhiShaderResourceBinding QRhiShaderResourceBinding::uniformBuffer(
\note \a size must be greater than 0.
\note \a buf must have been created with QRhiBuffer::UniformBuffer.
\note When \a buf is not null, it must have been created with
QRhiBuffer::UniformBuffer.
\note \a buf can be null. It is valid to create a
QRhiShaderResourceBindings with unspecified resources, but such an object
cannot be used with QRhiCommandBuffer::setShaderResources(). It is however
suitable for creating pipelines. Such a pipeline must then always be used
together with another, layout compatible QRhiShaderResourceBindings with
resources present passed to QRhiCommandBuffer::setShaderResources().
*/
QRhiShaderResourceBinding QRhiShaderResourceBinding::uniformBuffer(
int binding, StageFlags stage, QRhiBuffer *buf, int offset, int size)
@ -2901,7 +2917,15 @@ QRhiShaderResourceBinding QRhiShaderResourceBinding::uniformBuffer(
size of the bound region is specified by \a size. Like with non-dynamic
offsets, \c{offset + size} cannot exceed the size of \a buf.
\note \a buf must have been created with QRhiBuffer::UniformBuffer.
\note When \a buf is not null, it must have been created with
QRhiBuffer::UniformBuffer.
\note \a buf can be null. It is valid to create a
QRhiShaderResourceBindings with unspecified resources, but such an object
cannot be used with QRhiCommandBuffer::setShaderResources(). It is however
suitable for creating pipelines. Such a pipeline must then always be used
together with another, layout compatible QRhiShaderResourceBindings with
resources present passed to QRhiCommandBuffer::setShaderResources().
*/
QRhiShaderResourceBinding QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(
int binding, StageFlags stage, QRhiBuffer *buf, int size)
@ -2919,6 +2943,13 @@ QRhiShaderResourceBinding QRhiShaderResourceBinding::uniformBufferWithDynamicOff
\note This function is equivalent to calling sampledTextures() with a
\c count of 1.
\note \a tex and \a sampler can be null. It is valid to create a
QRhiShaderResourceBindings with unspecified resources, but such an object
cannot be used with QRhiCommandBuffer::setShaderResources(). It is however
suitable for creating pipelines. Such a pipeline must then always be used
together with another, layout compatible QRhiShaderResourceBindings with
resources present passed to QRhiCommandBuffer::setShaderResources().
\sa sampledTextures()
*/
QRhiShaderResourceBinding QRhiShaderResourceBinding::sampledTexture(
@ -2953,6 +2984,13 @@ QRhiShaderResourceBinding QRhiShaderResourceBinding::sampledTexture(
advised to provide "dummy" samplers and textures if some array elements are
not relevant (due to not being accessed in the shader).
\note \a texSamplers can be null. It is valid to create a
QRhiShaderResourceBindings with unspecified resources, but such an object
cannot be used with QRhiCommandBuffer::setShaderResources(). It is however
suitable for creating pipelines. Such a pipeline must then always be used
together with another, layout compatible QRhiShaderResourceBindings with
resources present passed to QRhiCommandBuffer::setShaderResources().
\sa sampledTexture()
*/
QRhiShaderResourceBinding QRhiShaderResourceBinding::sampledTextures(
@ -2964,8 +3002,12 @@ QRhiShaderResourceBinding QRhiShaderResourceBinding::sampledTextures(
b.d.stage = stage;
b.d.type = SampledTexture;
b.d.u.stex.count = count;
for (int i = 0; i < count; ++i)
b.d.u.stex.texSamplers[i] = texSamplers[i];
for (int i = 0; i < count; ++i) {
if (texSamplers)
b.d.u.stex.texSamplers[i] = texSamplers[i];
else
b.d.u.stex.texSamplers[i] = {};
}
return b;
}
@ -2975,7 +3017,15 @@ QRhiShaderResourceBinding QRhiShaderResourceBinding::sampledTextures(
will have access to all layers of the specified \a level. (so if the texture
is a cubemap, the shader must use imageCube instead of image2D)
\note \a tex must have been created with QRhiTexture::UsedWithLoadStore.
\note When \a tex is not null, it must have been created with
QRhiTexture::UsedWithLoadStore.
\note \a tex can be null. It is valid to create a QRhiShaderResourceBindings
with unspecified resources, but such an object cannot be used with
QRhiCommandBuffer::setShaderResources(). It is however suitable for creating
pipelines. Such a pipeline must then always be used together with another,
layout compatible QRhiShaderResourceBindings with resources present passed
to QRhiCommandBuffer::setShaderResources().
*/
QRhiShaderResourceBinding QRhiShaderResourceBinding::imageLoad(
int binding, StageFlags stage, QRhiTexture *tex, int level)
@ -2995,7 +3045,15 @@ QRhiShaderResourceBinding QRhiShaderResourceBinding::imageLoad(
will have access to all layers of the specified \a level. (so if the texture
is a cubemap, the shader must use imageCube instead of image2D)
\note \a tex must have been created with QRhiTexture::UsedWithLoadStore.
\note When \a tex is not null, it must have been created with
QRhiTexture::UsedWithLoadStore.
\note \a tex can be null. It is valid to create a QRhiShaderResourceBindings
with unspecified resources, but such an object cannot be used with
QRhiCommandBuffer::setShaderResources(). It is however suitable for creating
pipelines. Such a pipeline must then always be used together with another,
layout compatible QRhiShaderResourceBindings with resources present passed
to QRhiCommandBuffer::setShaderResources().
*/
QRhiShaderResourceBinding QRhiShaderResourceBinding::imageStore(
int binding, StageFlags stage, QRhiTexture *tex, int level)
@ -3011,7 +3069,15 @@ QRhiShaderResourceBinding QRhiShaderResourceBinding::imageStore(
will have access to all layers of the specified \a level. (so if the texture
is a cubemap, the shader must use imageCube instead of image2D)
\note \a tex must have been created with QRhiTexture::UsedWithLoadStore.
\note When \a tex is not null, it must have been created with
QRhiTexture::UsedWithLoadStore.
\note \a tex can be null. It is valid to create a QRhiShaderResourceBindings
with unspecified resources, but such an object cannot be used with
QRhiCommandBuffer::setShaderResources(). It is however suitable for creating
pipelines. Such a pipeline must then always be used together with another,
layout compatible QRhiShaderResourceBindings with resources present passed
to QRhiCommandBuffer::setShaderResources().
*/
QRhiShaderResourceBinding QRhiShaderResourceBinding::imageLoadStore(
int binding, StageFlags stage, QRhiTexture *tex, int level)
@ -3025,7 +3091,15 @@ QRhiShaderResourceBinding QRhiShaderResourceBinding::imageLoadStore(
\return a shader resource binding for a read-only storage buffer with the
given \a binding number and pipeline \a stage.
\note \a buf must have been created with QRhiBuffer::StorageBuffer.
\note When \a buf is not null, must have been created with
QRhiBuffer::StorageBuffer.
\note \a buf can be null. It is valid to create a
QRhiShaderResourceBindings with unspecified resources, but such an object
cannot be used with QRhiCommandBuffer::setShaderResources(). It is however
suitable for creating pipelines. Such a pipeline must then always be used
together with another, layout compatible QRhiShaderResourceBindings with
resources present passed to QRhiCommandBuffer::setShaderResources().
*/
QRhiShaderResourceBinding QRhiShaderResourceBinding::bufferLoad(
int binding, StageFlags stage, QRhiBuffer *buf)
@ -3045,7 +3119,15 @@ QRhiShaderResourceBinding QRhiShaderResourceBinding::bufferLoad(
given \a binding number and pipeline \a stage. This overload binds a region
only, as specified by \a offset and \a size.
\note \a buf must have been created with QRhiBuffer::StorageBuffer.
\note When \a buf is not null, must have been created with
QRhiBuffer::StorageBuffer.
\note \a buf can be null. It is valid to create a
QRhiShaderResourceBindings with unspecified resources, but such an object
cannot be used with QRhiCommandBuffer::setShaderResources(). It is however
suitable for creating pipelines. Such a pipeline must then always be used
together with another, layout compatible QRhiShaderResourceBindings with
resources present passed to QRhiCommandBuffer::setShaderResources().
*/
QRhiShaderResourceBinding QRhiShaderResourceBinding::bufferLoad(
int binding, StageFlags stage, QRhiBuffer *buf, int offset, int size)
@ -3061,7 +3143,15 @@ QRhiShaderResourceBinding QRhiShaderResourceBinding::bufferLoad(
\return a shader resource binding for a write-only storage buffer with the
given \a binding number and pipeline \a stage.
\note \a buf must have been created with QRhiBuffer::StorageBuffer.
\note When \a buf is not null, must have been created with
QRhiBuffer::StorageBuffer.
\note \a buf can be null. It is valid to create a
QRhiShaderResourceBindings with unspecified resources, but such an object
cannot be used with QRhiCommandBuffer::setShaderResources(). It is however
suitable for creating pipelines. Such a pipeline must then always be used
together with another, layout compatible QRhiShaderResourceBindings with
resources present passed to QRhiCommandBuffer::setShaderResources().
*/
QRhiShaderResourceBinding QRhiShaderResourceBinding::bufferStore(
int binding, StageFlags stage, QRhiBuffer *buf)
@ -3076,7 +3166,15 @@ QRhiShaderResourceBinding QRhiShaderResourceBinding::bufferStore(
given \a binding number and pipeline \a stage. This overload binds a region
only, as specified by \a offset and \a size.
\note \a buf must have been created with QRhiBuffer::StorageBuffer.
\note When \a buf is not null, must have been created with
QRhiBuffer::StorageBuffer.
\note \a buf can be null. It is valid to create a
QRhiShaderResourceBindings with unspecified resources, but such an object
cannot be used with QRhiCommandBuffer::setShaderResources(). It is however
suitable for creating pipelines. Such a pipeline must then always be used
together with another, layout compatible QRhiShaderResourceBindings with
resources present passed to QRhiCommandBuffer::setShaderResources().
*/
QRhiShaderResourceBinding QRhiShaderResourceBinding::bufferStore(
int binding, StageFlags stage, QRhiBuffer *buf, int offset, int size)
@ -3092,7 +3190,15 @@ QRhiShaderResourceBinding QRhiShaderResourceBinding::bufferStore(
\return a shader resource binding for a read-write storage buffer with the
given \a binding number and pipeline \a stage.
\note \a buf must have been created with QRhiBuffer::StorageBuffer.
\note When \a buf is not null, must have been created with
QRhiBuffer::StorageBuffer.
\note \a buf can be null. It is valid to create a
QRhiShaderResourceBindings with unspecified resources, but such an object
cannot be used with QRhiCommandBuffer::setShaderResources(). It is however
suitable for creating pipelines. Such a pipeline must then always be used
together with another, layout compatible QRhiShaderResourceBindings with
resources present passed to QRhiCommandBuffer::setShaderResources().
*/
QRhiShaderResourceBinding QRhiShaderResourceBinding::bufferLoadStore(
int binding, StageFlags stage, QRhiBuffer *buf)
@ -3107,7 +3213,15 @@ QRhiShaderResourceBinding QRhiShaderResourceBinding::bufferLoadStore(
given \a binding number and pipeline \a stage. This overload binds a region
only, as specified by \a offset and \a size.
\note \a buf must have been created with QRhiBuffer::StorageBuffer.
\note When \a buf is not null, must have been created with
QRhiBuffer::StorageBuffer.
\note \a buf can be null. It is valid to create a
QRhiShaderResourceBindings with unspecified resources, but such an object
cannot be used with QRhiCommandBuffer::setShaderResources(). It is however
suitable for creating pipelines. Such a pipeline must then always be used
together with another, layout compatible QRhiShaderResourceBindings with
resources present passed to QRhiCommandBuffer::setShaderResources().
*/
QRhiShaderResourceBinding QRhiShaderResourceBinding::bufferLoadStore(
int binding, StageFlags stage, QRhiBuffer *buf, int offset, int size)

View File

@ -3006,54 +3006,8 @@ bool QMetalShaderResourceBindings::create()
boundResourceData.resize(sortedBindings.count());
for (int i = 0, ie = sortedBindings.count(); i != ie; ++i) {
const QRhiShaderResourceBinding::Data *b = sortedBindings.at(i).data();
QMetalShaderResourceBindings::BoundResourceData &bd(boundResourceData[i]);
switch (b->type) {
case QRhiShaderResourceBinding::UniformBuffer:
{
QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, b->u.ubuf.buf);
bd.ubuf.id = bufD->m_id;
bd.ubuf.generation = bufD->generation;
}
break;
case QRhiShaderResourceBinding::SampledTexture:
{
const QRhiShaderResourceBinding::Data::SampledTextureData *data = &b->u.stex;
bd.stex.count = data->count;
for (int elem = 0; elem < data->count; ++elem) {
QMetalTexture *texD = QRHI_RES(QMetalTexture, data->texSamplers[elem].tex);
QMetalSampler *samplerD = QRHI_RES(QMetalSampler, data->texSamplers[elem].sampler);
bd.stex.d[elem].texId = texD->m_id;
bd.stex.d[elem].texGeneration = texD->generation;
bd.stex.d[elem].samplerId = samplerD->m_id;
bd.stex.d[elem].samplerGeneration = samplerD->generation;
}
}
break;
case QRhiShaderResourceBinding::ImageLoad:
case QRhiShaderResourceBinding::ImageStore:
case QRhiShaderResourceBinding::ImageLoadStore:
{
QMetalTexture *texD = QRHI_RES(QMetalTexture, b->u.simage.tex);
bd.simage.id = texD->m_id;
bd.simage.generation = texD->generation;
}
break;
case QRhiShaderResourceBinding::BufferLoad:
case QRhiShaderResourceBinding::BufferStore:
case QRhiShaderResourceBinding::BufferLoadStore:
{
QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, b->u.sbuf.buf);
bd.sbuf.id = bufD->m_id;
bd.sbuf.generation = bufD->generation;
}
break;
default:
Q_UNREACHABLE();
break;
}
}
for (BoundResourceData &bd : boundResourceData)
memset(&bd, 0, sizeof(BoundResourceData));
generation += 1;
return true;

View File

@ -2555,7 +2555,6 @@ void QRhiVulkan::updateShaderResourceBindings(QRhiShaderResourceBindings *srb, i
const bool updateAll = descSetIdx < 0;
int frameSlot = updateAll ? 0 : descSetIdx;
while (frameSlot < (updateAll ? QVK_FRAMES_IN_FLIGHT : descSetIdx + 1)) {
srbD->boundResourceData[frameSlot].resize(srbD->sortedBindings.count());
for (int i = 0, ie = srbD->sortedBindings.count(); i != ie; ++i) {
const QRhiShaderResourceBinding::Data *b = srbD->sortedBindings.at(i).data();
QVkShaderResourceBindings::BoundResourceData &bd(srbD->boundResourceData[frameSlot][i]);
@ -6188,7 +6187,11 @@ bool QVkShaderResourceBindings::create()
if (!rhiD->allocateDescriptorSet(&allocInfo, descSets, &poolIndex))
return false;
rhiD->updateShaderResourceBindings(this);
for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
boundResourceData[i].resize(sortedBindings.count());
for (BoundResourceData &bd : boundResourceData[i])
memset(&bd, 0, sizeof(BoundResourceData));
}
lastActiveFrameSlot = -1;
generation += 1;

View File

@ -91,6 +91,13 @@ private slots:
void resourceUpdateBatchRGBATextureMip();
void invalidPipeline_data();
void invalidPipeline();
void srbLayoutCompatibility_data();
void srbLayoutCompatibility();
void srbWithNoResource_data();
void srbWithNoResource();
void renderPassDescriptorCompatibility_data();
void renderPassDescriptorCompatibility();
void renderToTextureSimple_data();
void renderToTextureSimple();
void renderToTextureMip_data();
@ -103,14 +110,12 @@ private slots:
void renderToTextureArrayOfTexturedQuad();
void renderToTextureTexturedQuadAndUniformBuffer_data();
void renderToTextureTexturedQuadAndUniformBuffer();
void renderToTextureDeferredSrb_data();
void renderToTextureDeferredSrb();
void renderToWindowSimple_data();
void renderToWindowSimple();
void finishWithinSwapchainFrame_data();
void finishWithinSwapchainFrame();
void srbLayoutCompatibility_data();
void srbLayoutCompatibility();
void renderPassDescriptorCompatibility_data();
void renderPassDescriptorCompatibility();
private:
void setWindowType(QWindow *window, QRhi::Implementation impl);
@ -2212,6 +2217,150 @@ void tst_QRhi::renderToTextureTexturedQuadAndUniformBuffer()
QCOMPARE(result1.pixel(28, 178), empty);
}
void tst_QRhi::renderToTextureDeferredSrb_data()
{
rhiTestData();
}
void tst_QRhi::renderToTextureDeferredSrb()
{
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());
QScopedPointer<QRhiBuffer> ubuf(rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 64 + 4));
QVERIFY(ubuf->create());
QMatrix4x4 matrix;
updates->updateDynamicBuffer(ubuf.data(), 0, 64, matrix.constData());
float opacity = 0.5f;
updates->updateDynamicBuffer(ubuf.data(), 64, 4, &opacity);
// this is the specific thing to test here: an srb with null resources
const QRhiShaderResourceBinding::StageFlags commonVisibility = QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage;
QScopedPointer<QRhiShaderResourceBindings> layoutOnlySrb(rhi->newShaderResourceBindings());
layoutOnlySrb->setBindings({
QRhiShaderResourceBinding::uniformBuffer(0, commonVisibility, nullptr),
QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, nullptr, nullptr)
});
QVERIFY(layoutOnlySrb->create());
QScopedPointer<QRhiGraphicsPipeline> pipeline(rhi->newGraphicsPipeline());
pipeline->setTopology(QRhiGraphicsPipeline::TriangleStrip);
QShader vs = loadShader(":/data/textured.vert.qsb");
QVERIFY(vs.isValid());
QShader fs = loadShader(":/data/textured.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(layoutOnlySrb.data()); // no resources needed yet
pipeline->setRenderPassDescriptor(rpDesc.data());
QVERIFY(pipeline->create());
// another, layout compatible, srb with the actual resources
QScopedPointer<QRhiShaderResourceBindings> layoutCompatibleSrbWithResources(rhi->newShaderResourceBindings());
layoutCompatibleSrbWithResources->setBindings({
QRhiShaderResourceBinding::uniformBuffer(0, commonVisibility, ubuf.data()),
QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, inputTexture.data(), sampler.data())
});
QVERIFY(layoutCompatibleSrbWithResources->create());
cb->beginPass(rt.data(), Qt::black, { 1.0f, 0 }, updates);
cb->setGraphicsPipeline(pipeline.data());
cb->setShaderResources(layoutCompatibleSrbWithResources.data()); // here we must use the srb referencing the resources
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) {
@ -2630,6 +2779,48 @@ void tst_QRhi::srbLayoutCompatibility()
}
}
void tst_QRhi::srbWithNoResource_data()
{
rhiTestData();
}
void tst_QRhi::srbWithNoResource()
{
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 srb");
QScopedPointer<QRhiTexture> texture(rhi->newTexture(QRhiTexture::RGBA8, QSize(512, 512)));
QVERIFY(texture->create());
QScopedPointer<QRhiSampler> sampler(rhi->newSampler(QRhiSampler::Nearest, QRhiSampler::Nearest, QRhiSampler::None,
QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge));
QVERIFY(sampler->create());
QScopedPointer<QRhiBuffer> buf(rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 1024));
QVERIFY(buf->create());
{
QScopedPointer<QRhiShaderResourceBindings> srb1(rhi->newShaderResourceBindings());
srb1->setBindings({
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage, nullptr),
QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, nullptr, nullptr)
});
QVERIFY(srb1->create());
QScopedPointer<QRhiShaderResourceBindings> srb2(rhi->newShaderResourceBindings());
srb2->setBindings({
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage, buf.data()),
QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, texture.data(), sampler.data())
});
QVERIFY(srb2->create());
QVERIFY(srb1->isLayoutCompatible(srb2.data()));
QVERIFY(srb2->isLayoutCompatible(srb1.data()));
}
}
void tst_QRhi::renderPassDescriptorCompatibility_data()
{
rhiTestData();