diff options
Diffstat (limited to 'tests/auto/gui/rhi')
18 files changed, 311 insertions, 0 deletions
diff --git a/tests/auto/gui/rhi/qrhi/data/buildshaders.bat b/tests/auto/gui/rhi/qrhi/data/buildshaders.bat index 68d9bb8ae7..8518db8afc 100644 --- a/tests/auto/gui/rhi/qrhi/data/buildshaders.bat +++ b/tests/auto/gui/rhi/qrhi/data/buildshaders.bat @@ -11,3 +11,7 @@ qsb --glsl "150,120,100 es" --hlsl 50 -c --msl 12 -o textured.vert.qsb textured. 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 qsb --glsl "150,120,100 es" --hlsl 50 -c --msl 12 -o textured_multiubuf.frag.qsb textured_multiubuf.frag +qsb --glsl 320es,410 --msl 12 --msltess simpletess.vert -o simpletess.vert.qsb +qsb --glsl 320es,410 --msl 12 --tess-mode triangles simpletess.tesc -o simpletess.tesc.qsb +qsb --glsl 320es,410 --msl 12 --tess-vertex-count 3 simpletess.tese -o simpletess.tese.qsb +qsb --glsl 320es,410 --msl 12 simpletess.frag -o simpletess.frag.qsb diff --git a/tests/auto/gui/rhi/qrhi/data/simpletess.frag b/tests/auto/gui/rhi/qrhi/data/simpletess.frag new file mode 100644 index 0000000000..375587662f --- /dev/null +++ b/tests/auto/gui/rhi/qrhi/data/simpletess.frag @@ -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); +} diff --git a/tests/auto/gui/rhi/qrhi/data/simpletess.frag.qsb b/tests/auto/gui/rhi/qrhi/data/simpletess.frag.qsb Binary files differnew file mode 100644 index 0000000000..0f42103ac5 --- /dev/null +++ b/tests/auto/gui/rhi/qrhi/data/simpletess.frag.qsb diff --git a/tests/auto/gui/rhi/qrhi/data/simpletess.tesc b/tests/auto/gui/rhi/qrhi/data/simpletess.tesc new file mode 100644 index 0000000000..e192fc77c7 --- /dev/null +++ b/tests/auto/gui/rhi/qrhi/data/simpletess.tesc @@ -0,0 +1,22 @@ +#version 440 + +layout(vertices = 3) out; + +layout(location = 0) in vec3 inColor[]; +layout(location = 0) out vec3 outColor[]; +layout(location = 1) patch out float a_per_patch_output_variable; + +void main() +{ + if (gl_InvocationID == 0) { + gl_TessLevelOuter[0] = 4.0; + gl_TessLevelOuter[1] = 4.0; + gl_TessLevelOuter[2] = 4.0; + + gl_TessLevelInner[0] = 4.0; + } + + gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position; + outColor[gl_InvocationID] = inColor[gl_InvocationID]; + a_per_patch_output_variable = 1.0; +} diff --git a/tests/auto/gui/rhi/qrhi/data/simpletess.tesc.qsb b/tests/auto/gui/rhi/qrhi/data/simpletess.tesc.qsb Binary files differnew file mode 100644 index 0000000000..8c98d92c46 --- /dev/null +++ b/tests/auto/gui/rhi/qrhi/data/simpletess.tesc.qsb diff --git a/tests/auto/gui/rhi/qrhi/data/simpletess.tese b/tests/auto/gui/rhi/qrhi/data/simpletess.tese new file mode 100644 index 0000000000..17b348635a --- /dev/null +++ b/tests/auto/gui/rhi/qrhi/data/simpletess.tese @@ -0,0 +1,17 @@ +#version 440 + +layout(triangles, fractional_odd_spacing, ccw) in; + +layout(location = 0) in vec3 inColor[]; +layout(location = 0) out vec3 outColor; +layout(location = 1) patch in float a_per_patch_output_variable; + +layout(std140, binding = 0) uniform buf { + mat4 mvp; +}; + +void main() +{ + gl_Position = mvp * ((gl_TessCoord.x * gl_in[0].gl_Position) + (gl_TessCoord.y * gl_in[1].gl_Position) + (gl_TessCoord.z * gl_in[2].gl_Position)); + outColor = gl_TessCoord.x * inColor[0] + gl_TessCoord.y * inColor[1] + gl_TessCoord.z * inColor[2] * a_per_patch_output_variable; +} diff --git a/tests/auto/gui/rhi/qrhi/data/simpletess.tese.qsb b/tests/auto/gui/rhi/qrhi/data/simpletess.tese.qsb Binary files differnew file mode 100644 index 0000000000..8aa7632717 --- /dev/null +++ b/tests/auto/gui/rhi/qrhi/data/simpletess.tese.qsb diff --git a/tests/auto/gui/rhi/qrhi/data/simpletess.vert b/tests/auto/gui/rhi/qrhi/data/simpletess.vert new file mode 100644 index 0000000000..3838d2f3bb --- /dev/null +++ b/tests/auto/gui/rhi/qrhi/data/simpletess.vert @@ -0,0 +1,12 @@ +#version 440 + +layout(location = 0) in vec3 position; +layout(location = 1) in vec3 color; + +layout(location = 0) out vec3 v_color; + +void main() +{ + gl_Position = vec4(position, 1.0); + v_color = color; +} diff --git a/tests/auto/gui/rhi/qrhi/data/simpletess.vert.qsb b/tests/auto/gui/rhi/qrhi/data/simpletess.vert.qsb Binary files differnew file mode 100644 index 0000000000..ee90983e0b --- /dev/null +++ b/tests/auto/gui/rhi/qrhi/data/simpletess.vert.qsb diff --git a/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp b/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp index d1252f8abf..45aa8799f9 100644 --- a/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp +++ b/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp @@ -136,6 +136,9 @@ private slots: void renderToRgb10Texture_data(); void renderToRgb10Texture(); + void tessellation_data(); + void tessellation(); + private: void setWindowType(QWindow *window, QRhi::Implementation impl); @@ -4899,5 +4902,165 @@ void tst_QRhi::renderToRgb10Texture() QVERIFY(redCount > blueCount); // 1742 > 178 } +void tst_QRhi::tessellation_data() +{ + rhiTestData(); +} + +void tst_QRhi::tessellation() +{ + 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::Tessellation)) { + // From a Vulkan or Metal implementation we expect tessellation to work, + // even though it is optional (as per spec) for Vulkan. + QVERIFY(rhi->backend() != QRhi::Vulkan); + QVERIFY(rhi->backend() != QRhi::Metal); + QSKIP("Tessellation is not supported with this graphics API, skipping test"); + } + + if (rhi->backend() == QRhi::D3D11) + QSKIP("Skipping tessellation test on D3D for now, test assets not prepared for HLSL yet"); + + QScopedPointer<QRhiTexture> texture(rhi->newTexture(QRhiTexture::RGBA8, QSize(1280, 720), 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()); + + static const float triangleVertices[] = { + 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f, + -0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, + 0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, + }; + + QRhiResourceUpdateBatch *u = rhi->nextResourceUpdateBatch(); + QScopedPointer<QRhiBuffer> vbuf(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(triangleVertices))); + QVERIFY(vbuf->create()); + u->uploadStaticBuffer(vbuf.data(), triangleVertices); + + QScopedPointer<QRhiBuffer> ubuf(rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 64)); + QVERIFY(ubuf->create()); + + // Use the 3D API specific correction matrix that flips Y, so we can use + // the OpenGL-targeted vertex data and the tessellation winding order of + // counter-clockwise to get uniform results. + QMatrix4x4 mvp = rhi->clipSpaceCorrMatrix(); + u->updateDynamicBuffer(ubuf.data(), 0, 64, mvp.constData()); + + QScopedPointer<QRhiShaderResourceBindings> srb(rhi->newShaderResourceBindings()); + srb->setBindings({ + QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::TessellationEvaluationStage, ubuf.data()), + }); + QVERIFY(srb->create()); + + QScopedPointer<QRhiGraphicsPipeline> pipeline(rhi->newGraphicsPipeline()); + + pipeline->setTopology(QRhiGraphicsPipeline::Patches); + pipeline->setPatchControlPointCount(3); + + pipeline->setShaderStages({ + { QRhiShaderStage::Vertex, loadShader(":/data/simpletess.vert.qsb") }, + { QRhiShaderStage::TessellationControl, loadShader(":/data/simpletess.tesc.qsb") }, + { QRhiShaderStage::TessellationEvaluation, loadShader(":/data/simpletess.tese.qsb") }, + { QRhiShaderStage::Fragment, loadShader(":/data/simpletess.frag.qsb") } + }); + + pipeline->setCullMode(QRhiGraphicsPipeline::Back); // to ensure the winding order is correct + + // won't get the wireframe with OpenGL ES + if (rhi->isFeatureSupported(QRhi::NonFillPolygonMode)) + pipeline->setPolygonMode(QRhiGraphicsPipeline::Line); + + QRhiVertexInputLayout inputLayout; + inputLayout.setBindings({ + { 6 * sizeof(float) } + }); + inputLayout.setAttributes({ + { 0, 0, QRhiVertexInputAttribute::Float3, 0 }, + { 0, 1, QRhiVertexInputAttribute::Float3, 3 * sizeof(float) } + }); + + pipeline->setVertexInputLayout(inputLayout); + pipeline->setShaderResourceBindings(srb.data()); + pipeline->setRenderPassDescriptor(rpDesc.data()); + + QVERIFY(pipeline->create()); + + QRhiCommandBuffer *cb = nullptr; + QCOMPARE(rhi->beginOffscreenFrame(&cb), QRhi::FrameOpSuccess); + + cb->beginPass(rt.data(), Qt::black, { 1.0f, 0 }, u); + cb->setGraphicsPipeline(pipeline.data()); + cb->setViewport({ 0, 0, float(rt->pixelSize().width()), float(rt->pixelSize().height()) }); + cb->setShaderResources(); + QRhiCommandBuffer::VertexInput vbufBinding(vbuf.data(), 0); + cb->setVertexInput(0, 1, &vbufBinding); + cb->draw(3); + + 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); + }; + QRhiResourceUpdateBatch *readbackBatch = rhi->nextResourceUpdateBatch(); + readbackBatch->readBackTexture({ texture.data() }, &readResult); + cb->endPass(readbackBatch); + + rhi->endOffscreenFrame(); + + if (rhi->isYUpInFramebuffer()) // we used clipSpaceCorrMatrix so this is different from many other tests + result = std::move(result).mirrored(); + + QCOMPARE(result.size(), rt->pixelSize()); + + // cannot check rendering results with Null, because there is no rendering there + if (impl == QRhi::Null) + return; + + int redCount = 0, greenCount = 0, blueCount = 0; + for (int y = 0; y < result.height(); ++y) { + const quint32 *p = reinterpret_cast<const quint32 *>(result.constScanLine(y)); + int x = result.width() - 1; + while (x-- >= 0) { + const QRgb c(*p++); + const int red = qRed(c); + const int green = qGreen(c); + const int blue = qBlue(c); + // just count the color components that are above a certain threshold + if (red > 240) + ++redCount; + if (green > 240) + ++greenCount; + if (blue > 240) + ++blueCount; + } + } + + // Line drawing can be different between the 3D APIs. What we will check if + // the number of strong-enough r/g/b components above a certain threshold. + // That is good enough to ensure that something got rendered, i.e. that + // tessellation is not completely broken. + // + // For the record the actual values are something like: + // OpenGL (NVIDIA, Windows) 59 82 82 + // Metal (Intel, macOS 12.5) 59 79 79 + // Vulkan (NVIDIA, Windows) 71 85 85 + + QVERIFY(redCount > 50); + QVERIFY(blueCount > 50); + QVERIFY(greenCount > 50); +} + #include <tst_qrhi.moc> QTEST_MAIN(tst_QRhi) diff --git a/tests/auto/gui/rhi/qshader/data/metal_enabled_tessellation_v7.frag.qsb b/tests/auto/gui/rhi/qshader/data/metal_enabled_tessellation_v7.frag.qsb Binary files differnew file mode 100644 index 0000000000..4d49ede3ff --- /dev/null +++ b/tests/auto/gui/rhi/qshader/data/metal_enabled_tessellation_v7.frag.qsb diff --git a/tests/auto/gui/rhi/qshader/data/metal_enabled_tessellation_v7.tesc.qsb b/tests/auto/gui/rhi/qshader/data/metal_enabled_tessellation_v7.tesc.qsb Binary files differnew file mode 100644 index 0000000000..ea68da7eb4 --- /dev/null +++ b/tests/auto/gui/rhi/qshader/data/metal_enabled_tessellation_v7.tesc.qsb diff --git a/tests/auto/gui/rhi/qshader/data/metal_enabled_tessellation_v7.tese.qsb b/tests/auto/gui/rhi/qshader/data/metal_enabled_tessellation_v7.tese.qsb Binary files differnew file mode 100644 index 0000000000..41005f76bc --- /dev/null +++ b/tests/auto/gui/rhi/qshader/data/metal_enabled_tessellation_v7.tese.qsb diff --git a/tests/auto/gui/rhi/qshader/data/metal_enabled_tessellation_v7.vert.qsb b/tests/auto/gui/rhi/qshader/data/metal_enabled_tessellation_v7.vert.qsb Binary files differnew file mode 100644 index 0000000000..39734b6d5d --- /dev/null +++ b/tests/auto/gui/rhi/qshader/data/metal_enabled_tessellation_v7.vert.qsb diff --git a/tests/auto/gui/rhi/qshader/data/color.vert b/tests/auto/gui/rhi/qshader/data_src/color.vert index c92f71b9e1..c92f71b9e1 100644 --- a/tests/auto/gui/rhi/qshader/data/color.vert +++ b/tests/auto/gui/rhi/qshader/data_src/color.vert diff --git a/tests/auto/gui/rhi/qshader/data/texture.frag b/tests/auto/gui/rhi/qshader/data_src/texture.frag index bd22f817e0..bd22f817e0 100644 --- a/tests/auto/gui/rhi/qshader/data/texture.frag +++ b/tests/auto/gui/rhi/qshader/data_src/texture.frag diff --git a/tests/auto/gui/rhi/qshader/data/texture_sep.frag b/tests/auto/gui/rhi/qshader/data_src/texture_sep.frag index 368e851bb4..368e851bb4 100644 --- a/tests/auto/gui/rhi/qshader/data/texture_sep.frag +++ b/tests/auto/gui/rhi/qshader/data_src/texture_sep.frag diff --git a/tests/auto/gui/rhi/qshader/tst_qshader.cpp b/tests/auto/gui/rhi/qshader/tst_qshader.cpp index 40aa9d9a87..3065386ea9 100644 --- a/tests/auto/gui/rhi/qshader/tst_qshader.cpp +++ b/tests/auto/gui/rhi/qshader/tst_qshader.cpp @@ -25,6 +25,7 @@ private slots: void loadV4(); void manualShaderPackCreation(); void loadV6WithSeparateImagesAndSamplers(); + void loadV7(); }; static QShader getShader(const QString &name) @@ -590,5 +591,87 @@ void tst_QShader::loadV6WithSeparateImagesAndSamplers() } } +void tst_QShader::loadV7() +{ + QShader vert = getShader(QLatin1String(":/data/metal_enabled_tessellation_v7.vert.qsb")); + QVERIFY(vert.isValid()); + QCOMPARE(QShaderPrivate::get(&vert)->qsbVersion, 7); + QCOMPARE(vert.availableShaders().count(), 8); + + QCOMPARE(vert.description().inputVariables().count(), 2); + QCOMPARE(vert.description().outputBuiltinVariables().count(), 1); + QCOMPARE(vert.description().outputBuiltinVariables()[0].type, QShaderDescription::PositionBuiltin); + QCOMPARE(vert.description().outputVariables().count(), 1); + QCOMPARE(vert.description().outputVariables()[0].name, QByteArrayLiteral("v_color")); + + QVERIFY(vert.availableShaders().contains(QShaderKey(QShader::MslShader, QShaderVersion(12)))); + QVERIFY(!vert.shader(QShaderKey(QShader::MslShader, QShaderVersion(12), QShader::NonIndexedVertexAsComputeShader)).shader().isEmpty()); + QVERIFY(!vert.shader(QShaderKey(QShader::MslShader, QShaderVersion(12), QShader::UInt16IndexedVertexAsComputeShader)).shader().isEmpty()); + QVERIFY(!vert.shader(QShaderKey(QShader::MslShader, QShaderVersion(12), QShader::UInt32IndexedVertexAsComputeShader)).shader().isEmpty()); + + QShader tesc = getShader(QLatin1String(":/data/metal_enabled_tessellation_v7.tesc.qsb")); + QVERIFY(tesc.isValid()); + QCOMPARE(QShaderPrivate::get(&tesc)->qsbVersion, 7); + QCOMPARE(tesc.availableShaders().count(), 5); + QCOMPARE(tesc.description().tessellationOutputVertexCount(), 3); + + QCOMPARE(tesc.description().inputBuiltinVariables().count(), 2); + QCOMPARE(tesc.description().outputBuiltinVariables().count(), 3); + // builtins must be sorted based on the type + QCOMPARE(tesc.description().inputBuiltinVariables()[0].type, QShaderDescription::PositionBuiltin); + QCOMPARE(tesc.description().inputBuiltinVariables()[1].type, QShaderDescription::InvocationIdBuiltin); + QCOMPARE(tesc.description().outputBuiltinVariables()[0].type, QShaderDescription::PositionBuiltin); + QCOMPARE(tesc.description().outputBuiltinVariables()[1].type, QShaderDescription::TessLevelOuterBuiltin); + QCOMPARE(tesc.description().outputBuiltinVariables()[2].type, QShaderDescription::TessLevelInnerBuiltin); + + QCOMPARE(tesc.description().outputVariables().count(), 3); + for (const QShaderDescription::InOutVariable &v : tesc.description().outputVariables()) { + switch (v.location) { + case 0: + QCOMPARE(v.name, QByteArrayLiteral("outColor")); + QCOMPARE(v.type, QShaderDescription::Vec3); + QCOMPARE(v.perPatch, false); + break; + case 1: + QCOMPARE(v.name, QByteArrayLiteral("stuff")); + QCOMPARE(v.type, QShaderDescription::Vec3); + QCOMPARE(v.perPatch, true); + break; + case 2: + QCOMPARE(v.name, QByteArrayLiteral("more_stuff")); + QCOMPARE(v.type, QShaderDescription::Float); + QCOMPARE(v.perPatch, true); + break; + default: + QFAIL(qPrintable(QStringLiteral("Bad location: %1").arg(v.location))); + break; + } + } + + QVERIFY(!tesc.shader(QShaderKey(QShader::MslShader, QShaderVersion(12))).shader().isEmpty()); + QCOMPARE(tesc.nativeShaderInfo(QShaderKey(QShader::SpirvShader, QShaderVersion(100))).extraBufferBindings.count(), 0); + QCOMPARE(tesc.nativeShaderInfo(QShaderKey(QShader::MslShader, QShaderVersion(12))).extraBufferBindings.count(), 5); + + QShader tese = getShader(QLatin1String(":/data/metal_enabled_tessellation_v7.tese.qsb")); + QVERIFY(tese.isValid()); + QCOMPARE(QShaderPrivate::get(&tese)->qsbVersion, 7); + QCOMPARE(tese.availableShaders().count(), 5); + QCOMPARE(tese.description().tessellationMode(), QShaderDescription::TrianglesTessellationMode); + QCOMPARE(tese.description().tessellationWindingOrder(), QShaderDescription::CcwTessellationWindingOrder); + QCOMPARE(tese.description().tessellationPartitioning(), QShaderDescription::FractionalOddTessellationPartitioning); + + QCOMPARE(tese.description().inputBuiltinVariables()[0].type, QShaderDescription::PositionBuiltin); + QCOMPARE(tese.description().inputBuiltinVariables()[1].type, QShaderDescription::TessLevelOuterBuiltin); + QCOMPARE(tese.description().inputBuiltinVariables()[2].type, QShaderDescription::TessLevelInnerBuiltin); + QCOMPARE(tese.description().inputBuiltinVariables()[3].type, QShaderDescription::TessCoordBuiltin); + + QCOMPARE(tese.nativeResourceBindingMap(QShaderKey(QShader::MslShader, QShaderVersion(12))).count(), 1); + QCOMPARE(tese.nativeResourceBindingMap(QShaderKey(QShader::MslShader, QShaderVersion(12))).value(0), qMakePair(0, -1)); + + QShader frag = getShader(QLatin1String(":/data/metal_enabled_tessellation_v7.frag.qsb")); + QVERIFY(frag.isValid()); + QCOMPARE(QShaderPrivate::get(&frag)->qsbVersion, 7); +} + #include <tst_qshader.moc> QTEST_MAIN(tst_QShader) |