rhi: Add an autotest for multiview

Fixes: QTBUG-119742
Change-Id: Id4dba72eadfac74e1dd9ef57d90774c6a8bf8bdd
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Andy Nichols <andy.nichols@qt.io>
This commit is contained in:
Laszlo Agocs 2023-12-05 15:34:56 +01:00
parent 75f5eec2c9
commit cf6e018a17
6 changed files with 210 additions and 1 deletions

View File

@ -26,3 +26,5 @@ qsb --glsl 320es,430 --msl 21 --msltess tessinterfaceblocks.vert -o tessinterfac
qsb --glsl 320es,430 --msl 21 --tess-mode triangles tessinterfaceblocks.tesc -o tessinterfaceblocks.tesc.qsb
qsb --glsl 320es,430 --msl 21 --tess-vertex-count 3 tessinterfaceblocks.tese -o tessinterfaceblocks.tese.qsb
qsb --glsl 320es,430 --msl 21 simpletess.frag -o tessinterfaceblocks.frag.qsb
qsb --view-count 2 --glsl "300 es,330" --hlsl 61 -c --msl 12 multiview.vert -o multiview.vert.qsb
qsb --glsl "300 es,330" --hlsl 61 -c --msl 12 multiview.frag -o multiview.frag.qsb

View File

@ -0,0 +1,10 @@
#version 440
layout(location = 0) in vec3 v_color;
layout(location = 0) out vec4 fragColor;
void main()
{
fragColor = vec4(v_color, 1.0);
}

Binary file not shown.

View File

@ -0,0 +1,18 @@
#version 440
#extension GL_EXT_multiview : require
layout(location = 0) in vec4 pos;
layout(location = 1) in vec3 color;
layout(location = 0) out vec3 v_color;
layout(std140, binding = 0) uniform buf
{
mat4 mvp[2];
};
void main()
{
v_color = color;
gl_Position = mvp[gl_ViewIndex] * pos;
}

Binary file not shown.

View File

@ -115,6 +115,8 @@ private slots:
void renderToTextureSrbReuse();
void renderToTextureIndexedDraw_data();
void renderToTextureIndexedDraw();
void renderToTextureArrayMultiView_data();
void renderToTextureArrayMultiView();
void renderToWindowSimple_data();
void renderToWindowSimple();
void finishWithinSwapchainFrame_data();
@ -421,7 +423,8 @@ void tst_QRhi::create()
QRhi::OneDimensionalTextureMipmaps,
QRhi::HalfAttributes,
QRhi::RenderToOneDimensionalTexture,
QRhi::ThreeDimensionalTextureMipmaps
QRhi::ThreeDimensionalTextureMipmaps,
QRhi::MultiView
};
for (size_t i = 0; i <sizeof(features) / sizeof(QRhi::Feature); ++i)
rhi->isFeatureSupported(features[i]);
@ -3703,6 +3706,182 @@ void tst_QRhi::renderToTextureIndexedDraw()
QVERIFY(redCount > blueCount);
}
void tst_QRhi::renderToTextureArrayMultiView_data()
{
rhiTestData();
}
void tst_QRhi::renderToTextureArrayMultiView()
{
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");
if (!rhi->isFeatureSupported(QRhi::MultiView))
QSKIP("Multiview not supported, skipping testing on this backend");
if (rhi->backend() == QRhi::Vulkan && rhi->driverInfo().deviceType == QRhiDriverInfo::CpuDevice)
QSKIP("lavapipe does not like multiview, skip for now");
for (int sampleCount : rhi->supportedSampleCounts()) {
const QSize outputSize(1920, 1080);
QRhiTexture::Flags textureFlags = QRhiTexture::RenderTarget;
if (sampleCount <= 1)
textureFlags |= QRhiTexture::UsedAsTransferSource;
QScopedPointer<QRhiTexture> texture(rhi->newTextureArray(QRhiTexture::RGBA8, 2, outputSize, sampleCount, textureFlags));
QVERIFY(texture->create());
// exercise a depth-stencil buffer as well, not that the triangle needs it; note that this also needs to be a two-layer texture array
QScopedPointer<QRhiTexture> ds(rhi->newTextureArray(QRhiTexture::D24S8, 2, outputSize, sampleCount, QRhiTexture::RenderTarget));
QVERIFY(ds->create());
QScopedPointer<QRhiTexture> resolveTexture;
if (sampleCount > 1) {
resolveTexture.reset(rhi->newTextureArray(QRhiTexture::RGBA8, 2, outputSize, 1, QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource));
QVERIFY(resolveTexture->create());
}
QRhiColorAttachment multiViewAtt(texture.get());
multiViewAtt.setMultiViewCount(2);
if (sampleCount > 1)
multiViewAtt.setResolveTexture(resolveTexture.get());
QRhiTextureRenderTargetDescription rtDesc(multiViewAtt);
rtDesc.setDepthTexture(ds.get());
QScopedPointer<QRhiTextureRenderTarget> rt(rhi->newTextureRenderTarget(rtDesc));
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 float triangleData[] = {
0.0f, 0.5f, 1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, 0.0f, 1.0f, 0.0f,
0.5f, -0.5f, 0.0f, 0.0f, 1.0f
};
QScopedPointer<QRhiBuffer> vbuf(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(triangleData)));
QVERIFY(vbuf->create());
updates->uploadStaticBuffer(vbuf.data(), triangleData);
QScopedPointer<QRhiBuffer> ubuf(rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 128)); // mat4 mvp[2]
QVERIFY(ubuf->create());
QScopedPointer<QRhiShaderResourceBindings> srb(rhi->newShaderResourceBindings());
srb->setBindings({
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, ubuf.get())
});
QVERIFY(srb->create());
QScopedPointer<QRhiGraphicsPipeline> ps(rhi->newGraphicsPipeline());
ps->setShaderStages({
{ QRhiShaderStage::Vertex, loadShader(":/data/multiview.vert.qsb") },
{ QRhiShaderStage::Fragment, loadShader(":/data/multiview.frag.qsb") }
});
ps->setMultiViewCount(2); // the view count must be set both on the render target and the pipeline
QRhiVertexInputLayout inputLayout;
inputLayout.setBindings({
{ 5 * sizeof(float) }
});
inputLayout.setAttributes({
{ 0, 0, QRhiVertexInputAttribute::Float2, 0 },
{ 0, 1, QRhiVertexInputAttribute::Float3, quint32(2 * sizeof(float)) }
});
ps->setDepthTest(true);
ps->setDepthWrite(true);
ps->setSampleCount(sampleCount);
ps->setVertexInputLayout(inputLayout);
ps->setShaderResourceBindings(srb.get());
ps->setRenderPassDescriptor(rpDesc.get());
QVERIFY(ps->create());
QMatrix4x4 mvp = rhi->clipSpaceCorrMatrix();
mvp.perspective(45.0f, outputSize.width() / float(outputSize.height()), 0.01f, 1000.0f);
mvp.translate(0, 0, -2);
mvp.rotate(90, 0, 0, 1); // point left
updates->updateDynamicBuffer(ubuf.get(), 0, 64, mvp.constData());
mvp.rotate(-180, 0, 0, 1); // point right
updates->updateDynamicBuffer(ubuf.get(), 64, 64, mvp.constData());
cb->beginPass(rt.data(), Qt::black, { 1.0f, 0 }, updates);
cb->setGraphicsPipeline(ps.data());
cb->setShaderResources();
cb->setViewport({ 0, 0, float(outputSize.width()), float(outputSize.height()) });
QRhiCommandBuffer::VertexInput vbindings(vbuf.data(), 0);
cb->setVertexInput(0, 1, &vbindings);
cb->draw(3);
QRhiResourceUpdateBatch *readbackBatch = rhi->nextResourceUpdateBatch();
QRhiReadbackResult readResult[2];
QRhiReadbackDescription readbackDesc;
if (sampleCount > 1)
readbackDesc.setTexture(resolveTexture.get());
else
readbackDesc.setTexture(texture.get());
readbackDesc.setLayer(0);
readbackBatch->readBackTexture(readbackDesc, &readResult[0]);
readbackDesc.setLayer(1);
readbackBatch->readBackTexture(readbackDesc, &readResult[1]);
cb->endPass(readbackBatch);
rhi->endOffscreenFrame();
if (rhi->backend() == QRhi::Null)
QSKIP("No real content with Null backend, skipping multiview content check");
// both readbacks should be finished now due to using offscreen frames
QImage image0 = QImage(reinterpret_cast<const uchar *>(readResult[0].data.constData()),
readResult[0].pixelSize.width(), readResult[0].pixelSize.height(),
QImage::Format_RGBA8888);
if (rhi->isYUpInFramebuffer()) // note that we used clipSpaceCorrMatrix
image0 = image0.mirrored();
QImage image1 = QImage(reinterpret_cast<const uchar *>(readResult[1].data.constData()),
readResult[1].pixelSize.width(), readResult[1].pixelSize.height(),
QImage::Format_RGBA8888);
if (rhi->isYUpInFramebuffer())
image1 = image1.mirrored();
QVERIFY(!image0.isNull());
QVERIFY(!image1.isNull());
// image0 should have a triangle rotated so that it points left with the red
// tip. image1 should have a triangle rotated so that it points right with
// the red tip. Both are centered, so we will check in range 0..width/2 for
// image0 and width/2..width-1 for image1 to see if the red-enough pixels
// are present.
int y = image0.height() / 2;
int n = 0;
for (int x = 0; x < image0.width() / 2; ++x) {
QRgb c = image0.pixel(x, y);
if (qRed(c) > 250 && qGreen(c) < 10 && qBlue(c) < 10)
++n;
}
QVERIFY(n >= 10);
y = image1.height() / 2;
n = 0;
for (int x = image1.width() / 2; x < image1.width(); ++x) {
QRgb c = image1.pixel(x, y);
if (qRed(c) > 250 && qGreen(c) < 10 && qBlue(c) < 10)
++n;
}
QVERIFY(n >= 10);
}
}
void tst_QRhi::renderToWindowSimple_data()
{
rhiTestData();