summaryrefslogtreecommitdiffstats
path: root/tests/auto/gui/rhi
diff options
context:
space:
mode:
authorLaszlo Agocs <laszlo.agocs@qt.io>2021-12-21 13:26:45 +0100
committerLaszlo Agocs <laszlo.agocs@qt.io>2022-01-06 14:56:25 +0100
commit0a59101495634a02e7b893682904f4cfc5898624 (patch)
treeaadaf9ee66173de05d85579dc85338daa48a4a84 /tests/auto/gui/rhi
parentc20b213eabd8138f8566c7a1fd0633625c47b520 (diff)
rhi: Add support for separate image and sampler objects
For Direct 3D, Metal, and Vulkan this is natively supported. (and makes no difference in particular for D3D and Metal because they do not have the legacy combined image sampler concept anyways) With OpenGL it will work too, but this relies on SPIR-Cross magic and is still using a combined sampler (e.g. a sampler2D) in the GLSL shader. The GL backend walks back and forth in the mapping tables from the shader baker in order to make this work, which is presumably slightly more expensive than combined image samplers. Do note that combined image samplers (i.e. sampler2D in the shader and QRhiShaderResourceBinding::sampledTexture() in code) continue to be the primary, recommended way for any user of the rhi for the time being. Change-Id: I194721bc657b1ffbcc1bb79e6eadebe569a25087 Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Laszlo Agocs <laszlo.agocs@qt.io>
Diffstat (limited to 'tests/auto/gui/rhi')
-rw-r--r--tests/auto/gui/rhi/qrhi/BLACKLIST3
-rw-r--r--tests/auto/gui/rhi/qrhi/data/buildshaders.bat1
-rw-r--r--tests/auto/gui/rhi/qrhi/data/simpletextured_separate.frag14
-rw-r--r--tests/auto/gui/rhi/qrhi/data/simpletextured_separate.frag.qsbbin0 -> 1258 bytes
-rw-r--r--tests/auto/gui/rhi/qrhi/tst_qrhi.cpp127
5 files changed, 145 insertions, 0 deletions
diff --git a/tests/auto/gui/rhi/qrhi/BLACKLIST b/tests/auto/gui/rhi/qrhi/BLACKLIST
index 65c9e4834b..4e7b7dbc29 100644
--- a/tests/auto/gui/rhi/qrhi/BLACKLIST
+++ b/tests/auto/gui/rhi/qrhi/BLACKLIST
@@ -10,3 +10,6 @@ android
# Same here, GLES 3.0 features seem hopeless
[renderToTextureTextureArray]
android
+# Ditto
+[renderToTextureSampleWithSeparateTextureAndSampler]
+android
diff --git a/tests/auto/gui/rhi/qrhi/data/buildshaders.bat b/tests/auto/gui/rhi/qrhi/data/buildshaders.bat
index 0102457b8a..b2348b42f5 100644
--- a/tests/auto/gui/rhi/qrhi/data/buildshaders.bat
+++ b/tests/auto/gui/rhi/qrhi/data/buildshaders.bat
@@ -42,6 +42,7 @@ qsb --glsl "150,120,100 es" --hlsl 50 -c --msl 12 -o simple.frag.qsb simple.frag
qsb --glsl "150,120,100 es" --hlsl 50 -c --msl 12 -o simpletextured.vert.qsb simpletextured.vert
qsb --glsl "150,120,100 es" --hlsl 50 -c --msl 12 -o simpletextured.frag.qsb simpletextured.frag
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 simpletextured_separate.frag.qsb simpletextured_separate.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
diff --git a/tests/auto/gui/rhi/qrhi/data/simpletextured_separate.frag b/tests/auto/gui/rhi/qrhi/data/simpletextured_separate.frag
new file mode 100644
index 0000000000..41b0e4f1c2
--- /dev/null
+++ b/tests/auto/gui/rhi/qrhi/data/simpletextured_separate.frag
@@ -0,0 +1,14 @@
+#version 440
+
+layout(location = 0) in vec2 uv;
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 3) uniform texture2D tex;
+layout(binding = 5) uniform sampler samp;
+
+void main()
+{
+ vec4 c = texture(sampler2D(tex, samp), uv);
+ c.rgb *= c.a;
+ fragColor = c;
+}
diff --git a/tests/auto/gui/rhi/qrhi/data/simpletextured_separate.frag.qsb b/tests/auto/gui/rhi/qrhi/data/simpletextured_separate.frag.qsb
new file mode 100644
index 0000000000..c5afe1a8eb
--- /dev/null
+++ b/tests/auto/gui/rhi/qrhi/data/simpletextured_separate.frag.qsb
Binary files differ
diff --git a/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp b/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp
index 770d4be291..6a06fc4ef5 100644
--- a/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp
+++ b/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp
@@ -117,6 +117,8 @@ private slots:
void renderToTextureTextureArray();
void renderToTextureTexturedQuad_data();
void renderToTextureTexturedQuad();
+ void renderToTextureSampleWithSeparateTextureAndSampler_data();
+ void renderToTextureSampleWithSeparateTextureAndSampler();
void renderToTextureArrayOfTexturedQuad_data();
void renderToTextureArrayOfTexturedQuad();
void renderToTextureTexturedQuadAndUniformBuffer_data();
@@ -2157,6 +2159,131 @@ void tst_QRhi::renderToTextureTexturedQuad()
QVERIFY(qGreen(result.pixel(214, 191)) > 2 * qBlue(result.pixel(214, 191)));
}
+void tst_QRhi::renderToTextureSampleWithSeparateTextureAndSampler_data()
+{
+ rhiTestData();
+}
+
+void tst_QRhi::renderToTextureSampleWithSeparateTextureAndSampler()
+{
+ // Same as renderToTextureTexturedQuad but the fragment shader uses a
+ // separate image and sampler. For Vulkan/Metal/D3D11 these are natively
+ // supported. For OpenGL this exercises the auto-generated combined sampler
+ // in the GLSL code and the mapping table that gets applied at run time by
+ // the backend.
+
+ 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();
+
+ QScopedPointer<QRhiBuffer> vbuf(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(quadVerticesUvs)));
+ QVERIFY(vbuf->create());
+ updates->uploadStaticBuffer(vbuf.data(), quadVerticesUvs);
+
+ 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<QRhiShaderResourceBindings> srb(rhi->newShaderResourceBindings());
+ srb->setBindings({
+ QRhiShaderResourceBinding::texture(3, QRhiShaderResourceBinding::FragmentStage, inputTexture.data()),
+ QRhiShaderResourceBinding::sampler(5, QRhiShaderResourceBinding::FragmentStage, sampler.data())
+ });
+ QVERIFY(srb->create());
+
+ QScopedPointer<QRhiGraphicsPipeline> pipeline(rhi->newGraphicsPipeline());
+ pipeline->setTopology(QRhiGraphicsPipeline::TriangleStrip);
+ QShader vs = loadShader(":/data/simpletextured.vert.qsb");
+ QVERIFY(vs.isValid());
+ QShader fs = loadShader(":/data/simpletextured_separate.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());
+ cb->setShaderResources();
+ 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();
+
+ QRgb white = qRgba(255, 255, 255, 255);
+ QCOMPARE(result.pixel(79, 77), white);
+ QCOMPARE(result.pixel(124, 81), white);
+ QCOMPARE(result.pixel(128, 149), white);
+ QCOMPARE(result.pixel(120, 189), white);
+ QCOMPARE(result.pixel(116, 185), white);
+
+ 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);
+
+ QVERIFY(qGreen(result.pixel(32, 52)) > 2 * qRed(result.pixel(32, 52)));
+ QVERIFY(qGreen(result.pixel(32, 52)) > 2 * qBlue(result.pixel(32, 52)));
+ QVERIFY(qGreen(result.pixel(214, 191)) > 2 * qRed(result.pixel(214, 191)));
+ QVERIFY(qGreen(result.pixel(214, 191)) > 2 * qBlue(result.pixel(214, 191)));
+}
+
void tst_QRhi::renderToTextureArrayOfTexturedQuad_data()
{
rhiTestData();