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:
parent
e8d5000026
commit
351d42175d
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
Loading…
x
Reference in New Issue
Block a user