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:
parent
75f5eec2c9
commit
cf6e018a17
@ -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
|
||||
|
10
tests/auto/gui/rhi/qrhi/data/multiview.frag
Normal file
10
tests/auto/gui/rhi/qrhi/data/multiview.frag
Normal 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);
|
||||
}
|
BIN
tests/auto/gui/rhi/qrhi/data/multiview.frag.qsb
Normal file
BIN
tests/auto/gui/rhi/qrhi/data/multiview.frag.qsb
Normal file
Binary file not shown.
18
tests/auto/gui/rhi/qrhi/data/multiview.vert
Normal file
18
tests/auto/gui/rhi/qrhi/data/multiview.vert
Normal 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;
|
||||
}
|
BIN
tests/auto/gui/rhi/qrhi/data/multiview.vert.qsb
Normal file
BIN
tests/auto/gui/rhi/qrhi/data/multiview.vert.qsb
Normal file
Binary file not shown.
@ -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();
|
||||
|
Loading…
x
Reference in New Issue
Block a user