diff options
Diffstat (limited to 'tests/auto/gui/rhi')
16 files changed, 702 insertions, 55 deletions
diff --git a/tests/auto/gui/rhi/qrhi/data/compile.bat b/tests/auto/gui/rhi/qrhi/data/buildshaders.bat index 5b8a77b833..5b8a77b833 100644 --- a/tests/auto/gui/rhi/qrhi/data/compile.bat +++ b/tests/auto/gui/rhi/qrhi/data/buildshaders.bat diff --git a/tests/auto/gui/rhi/qrhi/data/simple.frag.qsb b/tests/auto/gui/rhi/qrhi/data/simple.frag.qsb Binary files differindex 264b71ec0f..c235108d39 100644 --- a/tests/auto/gui/rhi/qrhi/data/simple.frag.qsb +++ b/tests/auto/gui/rhi/qrhi/data/simple.frag.qsb diff --git a/tests/auto/gui/rhi/qrhi/data/simple.vert.qsb b/tests/auto/gui/rhi/qrhi/data/simple.vert.qsb Binary files differindex 59080b60c6..68cfeb8f1d 100644 --- a/tests/auto/gui/rhi/qrhi/data/simple.vert.qsb +++ b/tests/auto/gui/rhi/qrhi/data/simple.vert.qsb diff --git a/tests/auto/gui/rhi/qrhi/data/simpletextured.frag.qsb b/tests/auto/gui/rhi/qrhi/data/simpletextured.frag.qsb Binary files differindex f302702aa9..397961c238 100644 --- a/tests/auto/gui/rhi/qrhi/data/simpletextured.frag.qsb +++ b/tests/auto/gui/rhi/qrhi/data/simpletextured.frag.qsb diff --git a/tests/auto/gui/rhi/qrhi/data/simpletextured.vert.qsb b/tests/auto/gui/rhi/qrhi/data/simpletextured.vert.qsb Binary files differindex e4f12bfb9e..a9067949a5 100644 --- a/tests/auto/gui/rhi/qrhi/data/simpletextured.vert.qsb +++ b/tests/auto/gui/rhi/qrhi/data/simpletextured.vert.qsb diff --git a/tests/auto/gui/rhi/qrhi/data/textured.frag.qsb b/tests/auto/gui/rhi/qrhi/data/textured.frag.qsb Binary files differindex 0a039137ec..018d732e2f 100644 --- a/tests/auto/gui/rhi/qrhi/data/textured.frag.qsb +++ b/tests/auto/gui/rhi/qrhi/data/textured.frag.qsb diff --git a/tests/auto/gui/rhi/qrhi/data/textured.vert.qsb b/tests/auto/gui/rhi/qrhi/data/textured.vert.qsb Binary files differindex 7853f77943..44454d226e 100644 --- a/tests/auto/gui/rhi/qrhi/data/textured.vert.qsb +++ b/tests/auto/gui/rhi/qrhi/data/textured.vert.qsb diff --git a/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp b/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp index 533d6b17b1..191260fd41 100644 --- a/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp +++ b/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp @@ -52,7 +52,7 @@ # define TST_D3D11 #endif -#ifdef Q_OS_DARWIN +#if defined(Q_OS_MACOS) || defined(Q_OS_IOS) # include <QtGui/private/qrhimetal_p.h> # define TST_MTL #endif @@ -73,6 +73,8 @@ private slots: void create(); void nativeHandles_data(); void nativeHandles(); + void nativeTexture_data(); + void nativeTexture(); void resourceUpdateBatchBuffer_data(); void resourceUpdateBatchBuffer(); void resourceUpdateBatchRGBATextureUpload_data(); @@ -91,6 +93,10 @@ private slots: void renderToTextureTexturedQuadAndUniformBuffer(); void renderToWindowSimple_data(); void renderToWindowSimple(); + void srbLayoutCompatibility_data(); + void srbLayoutCompatibility(); + void renderPassDescriptorCompatibility_data(); + void renderPassDescriptorCompatibility(); private: struct { @@ -375,56 +381,6 @@ void tst_QRhi::nativeHandles() } } - // QRhiTexture::nativeHandles() - { - QScopedPointer<QRhiTexture> tex(rhi->newTexture(QRhiTexture::RGBA8, QSize(512, 256))); - QVERIFY(tex->build()); - - const QRhiNativeHandles *texHandles = tex->nativeHandles(); - QVERIFY(texHandles); - - switch (impl) { - case QRhi::Null: - break; -#ifdef TST_VK - case QRhi::Vulkan: - { - const QRhiVulkanTextureNativeHandles *vkHandles = static_cast<const QRhiVulkanTextureNativeHandles *>(texHandles); - QVERIFY(vkHandles->image); - QVERIFY(vkHandles->layout >= 1); // VK_IMAGE_LAYOUT_GENERAL - QVERIFY(vkHandles->layout <= 8); // VK_IMAGE_LAYOUT_PREINITIALIZED - } - break; -#endif -#ifdef TST_GL - case QRhi::OpenGLES2: - { - const QRhiGles2TextureNativeHandles *glHandles = static_cast<const QRhiGles2TextureNativeHandles *>(texHandles); - QVERIFY(glHandles->texture); - } - break; -#endif -#ifdef TST_D3D11 - case QRhi::D3D11: - { - const QRhiD3D11TextureNativeHandles *d3dHandles = static_cast<const QRhiD3D11TextureNativeHandles *>(texHandles); - QVERIFY(d3dHandles->texture); - } - break; -#endif -#ifdef TST_MTL - case QRhi::Metal: - { - const QRhiMetalTextureNativeHandles *mtlHandles = static_cast<const QRhiMetalTextureNativeHandles *>(texHandles); - QVERIFY(mtlHandles->texture); - } - break; -#endif - default: - Q_ASSERT(false); - } - } - // QRhiCommandBuffer::nativeHandles() { QRhiCommandBuffer *cb = nullptr; @@ -524,6 +480,71 @@ void tst_QRhi::nativeHandles() } } +void tst_QRhi::nativeTexture_data() +{ + rhiTestData(); +} + +void tst_QRhi::nativeTexture() +{ + 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 native texture"); + + QScopedPointer<QRhiTexture> tex(rhi->newTexture(QRhiTexture::RGBA8, QSize(512, 256))); + QVERIFY(tex->build()); + + const QRhiTexture::NativeTexture nativeTex = tex->nativeTexture(); + + switch (impl) { + case QRhi::Null: + break; +#ifdef TST_VK + case QRhi::Vulkan: + { + auto *image = static_cast<const VkImage *>(nativeTex.object); + QVERIFY(image); + QVERIFY(*image); + QVERIFY(nativeTex.layout >= 1); // VK_IMAGE_LAYOUT_GENERAL + QVERIFY(nativeTex.layout <= 8); // VK_IMAGE_LAYOUT_PREINITIALIZED + } + break; +#endif +#ifdef TST_GL + case QRhi::OpenGLES2: + { + auto *textureId = static_cast<const uint *>(nativeTex.object); + QVERIFY(textureId); + QVERIFY(*textureId); + } + break; +#endif +#ifdef TST_D3D11 + case QRhi::D3D11: + { + auto *texture = static_cast<void * const *>(nativeTex.object); + QVERIFY(texture); + QVERIFY(*texture); + } + break; +#endif +#ifdef TST_MTL + case QRhi::Metal: + { + void * const * texture = (void * const *)nativeTex.object; + QVERIFY(texture); + QVERIFY(*texture); + } + break; +#endif + default: + Q_ASSERT(false); + } +} + static bool submitResourceUpdates(QRhi *rhi, QRhiResourceUpdateBatch *batch) { QRhiCommandBuffer *cb = nullptr; @@ -1728,5 +1749,318 @@ void tst_QRhi::renderToWindowSimple() QVERIFY(redCount < blueCount); } +void tst_QRhi::srbLayoutCompatibility_data() +{ + rhiTestData(); +} + +void tst_QRhi::srbLayoutCompatibility() +{ + 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 texture resource updates"); + + QScopedPointer<QRhiTexture> texture(rhi->newTexture(QRhiTexture::RGBA8, QSize(512, 512))); + QVERIFY(texture->build()); + QScopedPointer<QRhiSampler> sampler(rhi->newSampler(QRhiSampler::Nearest, QRhiSampler::Nearest, QRhiSampler::None, + QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge)); + QVERIFY(sampler->build()); + QScopedPointer<QRhiSampler> otherSampler(rhi->newSampler(QRhiSampler::Nearest, QRhiSampler::Nearest, QRhiSampler::None, + QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge)); + QVERIFY(otherSampler->build()); + QScopedPointer<QRhiBuffer> buf(rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 1024)); + QVERIFY(buf->build()); + QScopedPointer<QRhiBuffer> otherBuf(rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 256)); + QVERIFY(otherBuf->build()); + + // empty (compatible) + { + QScopedPointer<QRhiShaderResourceBindings> srb1(rhi->newShaderResourceBindings()); + QVERIFY(srb1->build()); + + QScopedPointer<QRhiShaderResourceBindings> srb2(rhi->newShaderResourceBindings()); + QVERIFY(srb2->build()); + + QVERIFY(srb1->isLayoutCompatible(srb2.data())); + QVERIFY(srb2->isLayoutCompatible(srb1.data())); + } + + // different count (not compatible) + { + QScopedPointer<QRhiShaderResourceBindings> srb1(rhi->newShaderResourceBindings()); + QVERIFY(srb1->build()); + + QScopedPointer<QRhiShaderResourceBindings> srb2(rhi->newShaderResourceBindings()); + srb2->setBindings({ + QRhiShaderResourceBinding::sampledTexture(0, QRhiShaderResourceBinding::FragmentStage, texture.data(), sampler.data()) + }); + QVERIFY(srb2->build()); + + QVERIFY(!srb1->isLayoutCompatible(srb2.data())); + QVERIFY(!srb2->isLayoutCompatible(srb1.data())); + } + + // full match (compatible) + { + QScopedPointer<QRhiShaderResourceBindings> srb1(rhi->newShaderResourceBindings()); + srb1->setBindings({ + QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage, buf.data()), + QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, texture.data(), sampler.data()) + }); + QVERIFY(srb1->build()); + + 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->build()); + + QVERIFY(srb1->isLayoutCompatible(srb2.data())); + QVERIFY(srb2->isLayoutCompatible(srb1.data())); + } + + // different visibility (not compatible) + { + QScopedPointer<QRhiShaderResourceBindings> srb1(rhi->newShaderResourceBindings()); + srb1->setBindings({ + QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, buf.data()), + }); + QVERIFY(srb1->build()); + + QScopedPointer<QRhiShaderResourceBindings> srb2(rhi->newShaderResourceBindings()); + srb2->setBindings({ + QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage, buf.data()), + }); + QVERIFY(srb2->build()); + + QVERIFY(!srb1->isLayoutCompatible(srb2.data())); + QVERIFY(!srb2->isLayoutCompatible(srb1.data())); + } + + // different binding points (not compatible) + { + QScopedPointer<QRhiShaderResourceBindings> srb1(rhi->newShaderResourceBindings()); + srb1->setBindings({ + QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage, buf.data()), + }); + QVERIFY(srb1->build()); + + QScopedPointer<QRhiShaderResourceBindings> srb2(rhi->newShaderResourceBindings()); + srb2->setBindings({ + QRhiShaderResourceBinding::uniformBuffer(1, QRhiShaderResourceBinding::VertexStage, buf.data()), + }); + QVERIFY(srb2->build()); + + QVERIFY(!srb1->isLayoutCompatible(srb2.data())); + QVERIFY(!srb2->isLayoutCompatible(srb1.data())); + } + + // different buffer region offset and size (compatible) + { + QScopedPointer<QRhiShaderResourceBindings> srb1(rhi->newShaderResourceBindings()); + srb1->setBindings({ + QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage, buf.data(), rhi->ubufAligned(1), 128), + QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, texture.data(), sampler.data()) + }); + QVERIFY(srb1->build()); + + 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->build()); + + QVERIFY(srb1->isLayoutCompatible(srb2.data())); + QVERIFY(srb2->isLayoutCompatible(srb1.data())); + } + + // different resources (compatible) + { + QScopedPointer<QRhiShaderResourceBindings> srb1(rhi->newShaderResourceBindings()); + srb1->setBindings({ + QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage, otherBuf.data()), + QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, texture.data(), otherSampler.data()) + }); + QVERIFY(srb1->build()); + + 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->build()); + + QVERIFY(srb1->isLayoutCompatible(srb2.data())); + QVERIFY(srb2->isLayoutCompatible(srb1.data())); + } +} + +void tst_QRhi::renderPassDescriptorCompatibility_data() +{ + rhiTestData(); +} + +void tst_QRhi::renderPassDescriptorCompatibility() +{ + 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 texture resource updates"); + + // Note that checking compatibility is only relevant with backends where + // there is a concept of renderpass descriptions (Vulkan, and partially + // Metal). It is perfectly fine for isCompatible() to always return true + // when that is not the case (D3D11, OpenGL). Hence the 'if (Vulkan or + // Metal)' for all the negative tests. Also note "partial" for Metal: + // resolve textures for examples have no effect on compatibility with Metal. + + // tex and tex2 have the same format + QScopedPointer<QRhiTexture> tex(rhi->newTexture(QRhiTexture::RGBA8, QSize(512, 512), 1, QRhiTexture::RenderTarget)); + QVERIFY(tex->build()); + QScopedPointer<QRhiTexture> tex2(rhi->newTexture(QRhiTexture::RGBA8, QSize(512, 512), 1, QRhiTexture::RenderTarget)); + QVERIFY(tex2->build()); + + QScopedPointer<QRhiRenderBuffer> ds(rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, QSize(512, 512))); + QVERIFY(ds->build()); + + // two texture rendertargets with tex and tex2 as color0 (compatible) + { + QScopedPointer<QRhiTextureRenderTarget> rt(rhi->newTextureRenderTarget({ tex.data() })); + QScopedPointer<QRhiRenderPassDescriptor> rpDesc(rt->newCompatibleRenderPassDescriptor()); + rt->setRenderPassDescriptor(rpDesc.data()); + QVERIFY(rt->build()); + + QScopedPointer<QRhiTextureRenderTarget> rt2(rhi->newTextureRenderTarget({ tex2.data() })); + QScopedPointer<QRhiRenderPassDescriptor> rpDesc2(rt2->newCompatibleRenderPassDescriptor()); + rt2->setRenderPassDescriptor(rpDesc2.data()); + QVERIFY(rt2->build()); + + QVERIFY(rpDesc->isCompatible(rpDesc2.data())); + QVERIFY(rpDesc2->isCompatible(rpDesc.data())); + } + + // two texture rendertargets with tex and tex2 as color0, and a depth-stencil attachment as well (compatible) + { + QRhiTextureRenderTargetDescription desc({ tex.data() }, ds.data()); + QScopedPointer<QRhiTextureRenderTarget> rt(rhi->newTextureRenderTarget(desc)); + QScopedPointer<QRhiRenderPassDescriptor> rpDesc(rt->newCompatibleRenderPassDescriptor()); + rt->setRenderPassDescriptor(rpDesc.data()); + QVERIFY(rt->build()); + + QScopedPointer<QRhiTextureRenderTarget> rt2(rhi->newTextureRenderTarget(desc)); + QScopedPointer<QRhiRenderPassDescriptor> rpDesc2(rt2->newCompatibleRenderPassDescriptor()); + rt2->setRenderPassDescriptor(rpDesc2.data()); + QVERIFY(rt2->build()); + + QVERIFY(rpDesc->isCompatible(rpDesc2.data())); + QVERIFY(rpDesc2->isCompatible(rpDesc.data())); + } + + // now one of them does not have the ds attachment (not compatible) + { + QScopedPointer<QRhiTextureRenderTarget> rt(rhi->newTextureRenderTarget({ { tex.data() }, ds.data() })); + QScopedPointer<QRhiRenderPassDescriptor> rpDesc(rt->newCompatibleRenderPassDescriptor()); + rt->setRenderPassDescriptor(rpDesc.data()); + QVERIFY(rt->build()); + + QScopedPointer<QRhiTextureRenderTarget> rt2(rhi->newTextureRenderTarget({ tex.data() })); + QScopedPointer<QRhiRenderPassDescriptor> rpDesc2(rt2->newCompatibleRenderPassDescriptor()); + rt2->setRenderPassDescriptor(rpDesc2.data()); + QVERIFY(rt2->build()); + + if (impl == QRhi::Vulkan || impl == QRhi::Metal) { + QVERIFY(!rpDesc->isCompatible(rpDesc2.data())); + QVERIFY(!rpDesc2->isCompatible(rpDesc.data())); + } + } + + if (rhi->isFeatureSupported(QRhi::MultisampleRenderBuffer)) { + // resolve attachments (compatible) + { + QScopedPointer<QRhiRenderBuffer> msaaRenderBuffer(rhi->newRenderBuffer(QRhiRenderBuffer::Color, QSize(512, 512), 4)); + QVERIFY(msaaRenderBuffer->build()); + QScopedPointer<QRhiRenderBuffer> msaaRenderBuffer2(rhi->newRenderBuffer(QRhiRenderBuffer::Color, QSize(512, 512), 4)); + QVERIFY(msaaRenderBuffer2->build()); + + QRhiColorAttachment colorAtt(msaaRenderBuffer.data()); // color0, multisample + colorAtt.setResolveTexture(tex.data()); // resolved into a non-msaa texture + QScopedPointer<QRhiTextureRenderTarget> rt(rhi->newTextureRenderTarget({ colorAtt })); + QScopedPointer<QRhiRenderPassDescriptor> rpDesc(rt->newCompatibleRenderPassDescriptor()); + rt->setRenderPassDescriptor(rpDesc.data()); + QVERIFY(rt->build()); + + QRhiColorAttachment colorAtt2(msaaRenderBuffer2.data()); // color0, multisample + colorAtt2.setResolveTexture(tex2.data()); // resolved into a non-msaa texture + QScopedPointer<QRhiTextureRenderTarget> rt2(rhi->newTextureRenderTarget({ colorAtt2 })); + QScopedPointer<QRhiRenderPassDescriptor> rpDesc2(rt2->newCompatibleRenderPassDescriptor()); + rt2->setRenderPassDescriptor(rpDesc2.data()); + QVERIFY(rt2->build()); + + QVERIFY(rpDesc->isCompatible(rpDesc2.data())); + QVERIFY(rpDesc2->isCompatible(rpDesc.data())); + } + + // missing resolve for one of them (not compatible) + { + QScopedPointer<QRhiRenderBuffer> msaaRenderBuffer(rhi->newRenderBuffer(QRhiRenderBuffer::Color, QSize(512, 512), 4)); + QVERIFY(msaaRenderBuffer->build()); + QScopedPointer<QRhiRenderBuffer> msaaRenderBuffer2(rhi->newRenderBuffer(QRhiRenderBuffer::Color, QSize(512, 512), 4)); + QVERIFY(msaaRenderBuffer2->build()); + + QRhiColorAttachment colorAtt(msaaRenderBuffer.data()); // color0, multisample + colorAtt.setResolveTexture(tex.data()); // resolved into a non-msaa texture + QScopedPointer<QRhiTextureRenderTarget> rt(rhi->newTextureRenderTarget({ colorAtt })); + QScopedPointer<QRhiRenderPassDescriptor> rpDesc(rt->newCompatibleRenderPassDescriptor()); + rt->setRenderPassDescriptor(rpDesc.data()); + QVERIFY(rt->build()); + + QRhiColorAttachment colorAtt2(msaaRenderBuffer2.data()); // color0, multisample + QScopedPointer<QRhiTextureRenderTarget> rt2(rhi->newTextureRenderTarget({ colorAtt2 })); + QScopedPointer<QRhiRenderPassDescriptor> rpDesc2(rt2->newCompatibleRenderPassDescriptor()); + rt2->setRenderPassDescriptor(rpDesc2.data()); + QVERIFY(rt2->build()); + + if (impl == QRhi::Vulkan) { // no Metal here + QVERIFY(!rpDesc->isCompatible(rpDesc2.data())); + QVERIFY(!rpDesc2->isCompatible(rpDesc.data())); + } + } + } else { + qDebug("Skipping multisample renderbuffer dependent tests"); + } + + if (rhi->isTextureFormatSupported(QRhiTexture::RGBA32F)) { + QScopedPointer<QRhiTexture> tex3(rhi->newTexture(QRhiTexture::RGBA32F, QSize(512, 512), 1, QRhiTexture::RenderTarget)); + QVERIFY(tex3->build()); + + // different texture formats (not compatible) + { + QScopedPointer<QRhiTextureRenderTarget> rt(rhi->newTextureRenderTarget({ tex.data() })); + QScopedPointer<QRhiRenderPassDescriptor> rpDesc(rt->newCompatibleRenderPassDescriptor()); + rt->setRenderPassDescriptor(rpDesc.data()); + QVERIFY(rt->build()); + + QScopedPointer<QRhiTextureRenderTarget> rt2(rhi->newTextureRenderTarget({ tex3.data() })); + QScopedPointer<QRhiRenderPassDescriptor> rpDesc2(rt2->newCompatibleRenderPassDescriptor()); + rt2->setRenderPassDescriptor(rpDesc2.data()); + QVERIFY(rt2->build()); + + if (impl == QRhi::Vulkan || impl == QRhi::Metal) { + QVERIFY(!rpDesc->isCompatible(rpDesc2.data())); + QVERIFY(!rpDesc2->isCompatible(rpDesc.data())); + } + } + } else { + qDebug("Skipping texture format dependent tests"); + } +} + #include <tst_qrhi.moc> QTEST_MAIN(tst_QRhi) diff --git a/tests/auto/gui/rhi/qshader/data/README b/tests/auto/gui/rhi/qshader/data/README new file mode 100644 index 0000000000..3d89f2a0c5 --- /dev/null +++ b/tests/auto/gui/rhi/qshader/data/README @@ -0,0 +1,15 @@ +Warning: Do NOT regenerate the .qsb files without proper planning and understanding +the following. + +Among other things, we are also testing backwards compatibility for QShader +deserialization. + +.qsb files with _v1 in the name were produced with an older qtshadertools +and have a QSB_VERSION of 1. + +Files with _v2 are generated with a newer qsb, those have QSB_VERSION 2. +The difference is the support for nativeResourceBindingMap() which is only +present in v2. + +Files with _v3 come from an even newer qsb, and have QSB_VERSION 3. The +difference to 2 is the use of CBOR instead of binary JSON for QShaderDescription. diff --git a/tests/auto/gui/rhi/qshader/data/color.vert.qsb b/tests/auto/gui/rhi/qshader/data/color_all_v1.vert.qsb Binary files differindex 7d02d823d2..7d02d823d2 100644 --- a/tests/auto/gui/rhi/qshader/data/color.vert.qsb +++ b/tests/auto/gui/rhi/qshader/data/color_all_v1.vert.qsb diff --git a/tests/auto/gui/rhi/qshader/data/color_simple.vert.qsb b/tests/auto/gui/rhi/qshader/data/color_spirv_v1.vert.qsb Binary files differindex c82ba7e8e7..c82ba7e8e7 100644 --- a/tests/auto/gui/rhi/qshader/data/color_simple.vert.qsb +++ b/tests/auto/gui/rhi/qshader/data/color_spirv_v1.vert.qsb diff --git a/tests/auto/gui/rhi/qshader/data/texture.frag b/tests/auto/gui/rhi/qshader/data/texture.frag new file mode 100644 index 0000000000..bd22f817e0 --- /dev/null +++ b/tests/auto/gui/rhi/qshader/data/texture.frag @@ -0,0 +1,16 @@ +#version 440 + +layout(location = 0) in vec2 qt_TexCoord; +layout(location = 0) out vec4 fragColor; + +layout(std140, binding = 0) uniform buf { + mat4 qt_Matrix; + float opacity; +} ubuf; + +layout(binding = 1) uniform sampler2D qt_Texture; + +void main() +{ + fragColor = texture(qt_Texture, qt_TexCoord) * ubuf.opacity; +} diff --git a/tests/auto/gui/rhi/qshader/data/texture_all_v2.frag.qsb b/tests/auto/gui/rhi/qshader/data/texture_all_v2.frag.qsb Binary files differnew file mode 100644 index 0000000000..79f5486945 --- /dev/null +++ b/tests/auto/gui/rhi/qshader/data/texture_all_v2.frag.qsb diff --git a/tests/auto/gui/rhi/qshader/data/texture_all_v3.frag.qsb b/tests/auto/gui/rhi/qshader/data/texture_all_v3.frag.qsb Binary files differnew file mode 100644 index 0000000000..b6e49aa03d --- /dev/null +++ b/tests/auto/gui/rhi/qshader/data/texture_all_v3.frag.qsb diff --git a/tests/auto/gui/rhi/qshader/data/texture_all_v4.frag.qsb b/tests/auto/gui/rhi/qshader/data/texture_all_v4.frag.qsb Binary files differnew file mode 100644 index 0000000000..4292d67b7c --- /dev/null +++ b/tests/auto/gui/rhi/qshader/data/texture_all_v4.frag.qsb diff --git a/tests/auto/gui/rhi/qshader/tst_qshader.cpp b/tests/auto/gui/rhi/qshader/tst_qshader.cpp index 21f0cc7895..ab7115b74a 100644 --- a/tests/auto/gui/rhi/qshader/tst_qshader.cpp +++ b/tests/auto/gui/rhi/qshader/tst_qshader.cpp @@ -40,6 +40,11 @@ private slots: void genVariants(); void shaderDescImplicitSharing(); void bakedShaderImplicitSharing(); + void mslResourceMapping(); + void loadV3(); + void serializeShaderDesc(); + void comparison(); + void loadV4(); }; static QShader getShader(const QString &name) @@ -53,8 +58,9 @@ static QShader getShader(const QString &name) void tst_QShader::simpleCompileCheckResults() { - QShader s = getShader(QLatin1String(":/data/color_simple.vert.qsb")); + QShader s = getShader(QLatin1String(":/data/color_spirv_v1.vert.qsb")); QVERIFY(s.isValid()); + QCOMPARE(QShaderPrivate::get(&s)->qsbVersion, 1); QCOMPARE(s.availableShaders().count(), 1); const QShaderCode shader = s.shader(QShaderKey(QShader::SpirvShader, @@ -125,10 +131,11 @@ void tst_QShader::simpleCompileCheckResults() void tst_QShader::genVariants() { - QShader s = getShader(QLatin1String(":/data/color.vert.qsb")); + QShader s = getShader(QLatin1String(":/data/color_all_v1.vert.qsb")); // spirv, glsl 100, glsl 330, glsl 120, hlsl 50, msl 12 // + batchable variants QVERIFY(s.isValid()); + QCOMPARE(QShaderPrivate::get(&s)->qsbVersion, 1); QCOMPARE(s.availableShaders().count(), 2 * 6); int batchableVariantCount = 0; @@ -149,8 +156,9 @@ void tst_QShader::genVariants() void tst_QShader::shaderDescImplicitSharing() { - QShader s = getShader(QLatin1String(":/data/color_simple.vert.qsb")); + QShader s = getShader(QLatin1String(":/data/color_spirv_v1.vert.qsb")); QVERIFY(s.isValid()); + QCOMPARE(QShaderPrivate::get(&s)->qsbVersion, 1); QCOMPARE(s.availableShaders().count(), 1); QVERIFY(s.availableShaders().contains(QShaderKey(QShader::SpirvShader, QShaderVersion(100)))); @@ -168,6 +176,7 @@ void tst_QShader::shaderDescImplicitSharing() QCOMPARE(d1.inputVariables().count(), 2); QCOMPARE(d1.outputVariables().count(), 1); QCOMPARE(d1.uniformBlocks().count(), 1); + QCOMPARE(d0, d1); d1.detach(); QVERIFY(QShaderDescriptionPrivate::get(&d0) != QShaderDescriptionPrivate::get(&d1)); @@ -177,12 +186,17 @@ void tst_QShader::shaderDescImplicitSharing() QCOMPARE(d1.inputVariables().count(), 2); QCOMPARE(d1.outputVariables().count(), 1); QCOMPARE(d1.uniformBlocks().count(), 1); + QCOMPARE(d0, d1); + + d1 = QShaderDescription(); + QVERIFY(d0 != d1); } void tst_QShader::bakedShaderImplicitSharing() { - QShader s0 = getShader(QLatin1String(":/data/color_simple.vert.qsb")); + QShader s0 = getShader(QLatin1String(":/data/color_spirv_v1.vert.qsb")); QVERIFY(s0.isValid()); + QCOMPARE(QShaderPrivate::get(&s0)->qsbVersion, 1); QCOMPARE(s0.availableShaders().count(), 1); QVERIFY(s0.availableShaders().contains(QShaderKey(QShader::SpirvShader, QShaderVersion(100)))); @@ -229,5 +243,273 @@ void tst_QShader::bakedShaderImplicitSharing() } } +void tst_QShader::mslResourceMapping() +{ + QShader s = getShader(QLatin1String(":/data/texture_all_v2.frag.qsb")); + QVERIFY(s.isValid()); + QCOMPARE(QShaderPrivate::get(&s)->qsbVersion, 2); + + const QVector<QShaderKey> availableShaders = s.availableShaders(); + QCOMPARE(availableShaders.count(), 7); + QVERIFY(availableShaders.contains(QShaderKey(QShader::SpirvShader, QShaderVersion(100)))); + QVERIFY(availableShaders.contains(QShaderKey(QShader::MslShader, QShaderVersion(12)))); + QVERIFY(availableShaders.contains(QShaderKey(QShader::HlslShader, QShaderVersion(50)))); + QVERIFY(availableShaders.contains(QShaderKey(QShader::GlslShader, QShaderVersion(100, QShaderVersion::GlslEs)))); + QVERIFY(availableShaders.contains(QShaderKey(QShader::GlslShader, QShaderVersion(120)))); + QVERIFY(availableShaders.contains(QShaderKey(QShader::GlslShader, QShaderVersion(150)))); + QVERIFY(availableShaders.contains(QShaderKey(QShader::GlslShader, QShaderVersion(330)))); + + const QShader::NativeResourceBindingMap *resMap = + s.nativeResourceBindingMap(QShaderKey(QShader::GlslShader, QShaderVersion(330))); + QVERIFY(!resMap); + + // The Metal shader must come with a mapping table for binding points 0 + // (uniform buffer) and 1 (combined image sampler mapped to a texture and + // sampler in the shader). + resMap = s.nativeResourceBindingMap(QShaderKey(QShader::MslShader, QShaderVersion(12))); + QVERIFY(resMap); + + QCOMPARE(resMap->count(), 2); + QCOMPARE(resMap->value(0).first, 0); // mapped to native buffer index 0 + QCOMPARE(resMap->value(1), qMakePair(0, 0)); // mapped to native texture index 0 and sampler index 0 +} + +void tst_QShader::loadV3() +{ + // qsb version 3: QShaderDescription is serialized as CBOR. Ensure the deserialized data is as expected. + QShader s = getShader(QLatin1String(":/data/texture_all_v3.frag.qsb")); + QVERIFY(s.isValid()); + QCOMPARE(QShaderPrivate::get(&s)->qsbVersion, 3); + + const QVector<QShaderKey> availableShaders = s.availableShaders(); + QCOMPARE(availableShaders.count(), 7); + QVERIFY(availableShaders.contains(QShaderKey(QShader::SpirvShader, QShaderVersion(100)))); + QVERIFY(availableShaders.contains(QShaderKey(QShader::MslShader, QShaderVersion(12)))); + QVERIFY(availableShaders.contains(QShaderKey(QShader::HlslShader, QShaderVersion(50)))); + QVERIFY(availableShaders.contains(QShaderKey(QShader::GlslShader, QShaderVersion(100, QShaderVersion::GlslEs)))); + QVERIFY(availableShaders.contains(QShaderKey(QShader::GlslShader, QShaderVersion(120)))); + QVERIFY(availableShaders.contains(QShaderKey(QShader::GlslShader, QShaderVersion(150)))); + QVERIFY(availableShaders.contains(QShaderKey(QShader::GlslShader, QShaderVersion(330)))); + + const QShaderDescription desc = s.description(); + QVERIFY(desc.isValid()); + QCOMPARE(desc.inputVariables().count(), 1); + for (const QShaderDescription::InOutVariable &v : desc.inputVariables()) { + switch (v.location) { + case 0: + QCOMPARE(v.name, QLatin1String("qt_TexCoord")); + QCOMPARE(v.type, QShaderDescription::Vec2); + break; + default: + QVERIFY(false); + break; + } + } + QCOMPARE(desc.outputVariables().count(), 1); + for (const QShaderDescription::InOutVariable &v : desc.outputVariables()) { + switch (v.location) { + case 0: + QCOMPARE(v.name, QLatin1String("fragColor")); + QCOMPARE(v.type, QShaderDescription::Vec4); + break; + default: + QVERIFY(false); + break; + } + } + QCOMPARE(desc.uniformBlocks().count(), 1); + const QShaderDescription::UniformBlock blk = desc.uniformBlocks().first(); + QCOMPARE(blk.blockName, QLatin1String("buf")); + QCOMPARE(blk.structName, QLatin1String("ubuf")); + QCOMPARE(blk.size, 68); + QCOMPARE(blk.binding, 0); + QCOMPARE(blk.descriptorSet, 0); + QCOMPARE(blk.members.count(), 2); + for (int i = 0; i < blk.members.count(); ++i) { + const QShaderDescription::BlockVariable v = blk.members[i]; + switch (i) { + case 0: + QCOMPARE(v.offset, 0); + QCOMPARE(v.size, 64); + QCOMPARE(v.name, QLatin1String("qt_Matrix")); + QCOMPARE(v.type, QShaderDescription::Mat4); + QCOMPARE(v.matrixStride, 16); + break; + case 1: + QCOMPARE(v.offset, 64); + QCOMPARE(v.size, 4); + QCOMPARE(v.name, QLatin1String("opacity")); + QCOMPARE(v.type, QShaderDescription::Float); + break; + default: + QVERIFY(false); + break; + } + } +} + +void tst_QShader::serializeShaderDesc() +{ + // default constructed QShaderDescription + { + QShaderDescription desc; + QVERIFY(!desc.isValid()); + + QByteArray data; + { + QBuffer buf(&data); + QDataStream ds(&buf); + QVERIFY(buf.open(QIODevice::WriteOnly)); + desc.serialize(&ds); + } + QVERIFY(!data.isEmpty()); + + { + QBuffer buf(&data); + QDataStream ds(&buf); + QVERIFY(buf.open(QIODevice::ReadOnly)); + QShaderDescription desc2 = QShaderDescription::deserialize(&ds); + QVERIFY(!desc2.isValid()); + } + } + + // a QShaderDescription with inputs, outputs, uniform block and combined image sampler + { + QShader s = getShader(QLatin1String(":/data/texture_all_v4.frag.qsb")); + QVERIFY(s.isValid()); + const QShaderDescription desc = s.description(); + QVERIFY(desc.isValid()); + + QByteArray data; + { + QBuffer buf(&data); + QDataStream ds(&buf); + QVERIFY(buf.open(QIODevice::WriteOnly)); + desc.serialize(&ds); + } + QVERIFY(!data.isEmpty()); + + { + QShaderDescription desc2; + QVERIFY(!desc2.isValid()); + QVERIFY(!(desc == desc2)); + QVERIFY(desc != desc2); + } + + { + QBuffer buf(&data); + QDataStream ds(&buf); + QVERIFY(buf.open(QIODevice::ReadOnly)); + QShaderDescription desc2 = QShaderDescription::deserialize(&ds); + QVERIFY(desc2.isValid()); + QCOMPARE(desc, desc2); + } + } +} + +void tst_QShader::comparison() +{ + // exercise QShader and QShaderDescription comparisons + { + QShader s1 = getShader(QLatin1String(":/data/texture_all_v4.frag.qsb")); + QVERIFY(s1.isValid()); + QShader s2 = getShader(QLatin1String(":/data/color_all_v1.vert.qsb")); + QVERIFY(s2.isValid()); + + QVERIFY(s1.description().isValid()); + QVERIFY(s2.description().isValid()); + + QVERIFY(s1 != s2); + QVERIFY(s1.description() != s2.description()); + } + + { + QShader s1 = getShader(QLatin1String(":/data/texture_all_v4.frag.qsb")); + QVERIFY(s1.isValid()); + QShader s2 = getShader(QLatin1String(":/data/texture_all_v4.frag.qsb")); + QVERIFY(s2.isValid()); + + QVERIFY(s1.description().isValid()); + QVERIFY(s2.description().isValid()); + + QVERIFY(s1 == s2); + QVERIFY(s1.description() == s2.description()); + } +} + +void tst_QShader::loadV4() +{ + // qsb version 4: QShaderDescription is serialized via QDataStream. Ensure the deserialized data is as expected. + QShader s = getShader(QLatin1String(":/data/texture_all_v4.frag.qsb")); + QVERIFY(s.isValid()); + QCOMPARE(QShaderPrivate::get(&s)->qsbVersion, 4); + + const QVector<QShaderKey> availableShaders = s.availableShaders(); + QCOMPARE(availableShaders.count(), 7); + QVERIFY(availableShaders.contains(QShaderKey(QShader::SpirvShader, QShaderVersion(100)))); + QVERIFY(availableShaders.contains(QShaderKey(QShader::MslShader, QShaderVersion(12)))); + QVERIFY(availableShaders.contains(QShaderKey(QShader::HlslShader, QShaderVersion(50)))); + QVERIFY(availableShaders.contains(QShaderKey(QShader::GlslShader, QShaderVersion(100, QShaderVersion::GlslEs)))); + QVERIFY(availableShaders.contains(QShaderKey(QShader::GlslShader, QShaderVersion(120)))); + QVERIFY(availableShaders.contains(QShaderKey(QShader::GlslShader, QShaderVersion(150)))); + QVERIFY(availableShaders.contains(QShaderKey(QShader::GlslShader, QShaderVersion(330)))); + + const QShaderDescription desc = s.description(); + QVERIFY(desc.isValid()); + QCOMPARE(desc.inputVariables().count(), 1); + for (const QShaderDescription::InOutVariable &v : desc.inputVariables()) { + switch (v.location) { + case 0: + QCOMPARE(v.name, QLatin1String("qt_TexCoord")); + QCOMPARE(v.type, QShaderDescription::Vec2); + break; + default: + QVERIFY(false); + break; + } + } + QCOMPARE(desc.outputVariables().count(), 1); + for (const QShaderDescription::InOutVariable &v : desc.outputVariables()) { + switch (v.location) { + case 0: + QCOMPARE(v.name, QLatin1String("fragColor")); + QCOMPARE(v.type, QShaderDescription::Vec4); + break; + default: + QVERIFY(false); + break; + } + } + QCOMPARE(desc.uniformBlocks().count(), 1); + const QShaderDescription::UniformBlock blk = desc.uniformBlocks().first(); + QCOMPARE(blk.blockName, QLatin1String("buf")); + QCOMPARE(blk.structName, QLatin1String("ubuf")); + QCOMPARE(blk.size, 68); + QCOMPARE(blk.binding, 0); + QCOMPARE(blk.descriptorSet, 0); + QCOMPARE(blk.members.count(), 2); + for (int i = 0; i < blk.members.count(); ++i) { + const QShaderDescription::BlockVariable v = blk.members[i]; + switch (i) { + case 0: + QCOMPARE(v.offset, 0); + QCOMPARE(v.size, 64); + QCOMPARE(v.name, QLatin1String("qt_Matrix")); + QCOMPARE(v.type, QShaderDescription::Mat4); + QCOMPARE(v.matrixStride, 16); + break; + case 1: + QCOMPARE(v.offset, 64); + QCOMPARE(v.size, 4); + QCOMPARE(v.name, QLatin1String("opacity")); + QCOMPARE(v.type, QShaderDescription::Float); + break; + default: + QVERIFY(false); + break; + } + } +} + #include <tst_qshader.moc> QTEST_MAIN(tst_QShader) |