Add test for rendercontrol + rendererinterface

Also add support for doing
QQuickWindow::setSceneGraphBackend(QSGRendererInterface::Unknown). This
becomes relevant especially in tests, where we may request a specific
graphics API, but then want to switch back to the default behavior.

Change-Id: Ib09c411432fc37bd4d36ce55d54d20af3f83860a
Reviewed-by: Andy Nichols <andy.nichols@qt.io>
This commit is contained in:
Laszlo Agocs 2020-06-22 12:26:23 +02:00
parent fdba8facd0
commit 38bd2bd831
3 changed files with 232 additions and 6 deletions

View File

@ -5592,7 +5592,7 @@ void QQuickWindow::setSceneGraphBackend(QSGRendererInterface::GraphicsApi api)
default:
break;
}
if (QSGRendererInterface::isApiRhiBased(api))
if (QSGRendererInterface::isApiRhiBased(api) || api == QSGRendererInterface::Unknown)
QSGRhiSupport::configure(api);
}

View File

@ -268,11 +268,18 @@ void QSGRhiSupport::checkEnvQSgInfo()
void QSGRhiSupport::configure(QSGRendererInterface::GraphicsApi api)
{
Q_ASSERT(QSGRendererInterface::isApiRhiBased(api));
QSGRhiSupport *inst = staticInst();
inst->m_requested.valid = true;
inst->m_requested.api = api;
inst->applySettings();
if (api == QSGRendererInterface::Unknown) {
// behave as if nothing was explicitly requested
QSGRhiSupport *inst = staticInst();
inst->m_requested.valid = false;
inst->applySettings();
} else {
Q_ASSERT(QSGRendererInterface::isApiRhiBased(api));
QSGRhiSupport *inst = staticInst();
inst->m_requested.valid = true;
inst->m_requested.api = api;
inst->applySettings();
}
}
QSGRhiSupport *QSGRhiSupport::instance()

View File

@ -44,13 +44,19 @@
#include <QSignalSpy>
#include <private/qquickwindow_p.h>
#include <private/qguiapplication_p.h>
#include <QtGui/qpa/qplatformintegration.h>
#include <QRunnable>
#include <QSGRendererInterface>
#include <QQuickRenderControl>
#include <QOperatingSystemVersion>
#include <functional>
#include <QtGui/private/qrhi_p.h>
#if QT_CONFIG(opengl)
#include <QOpenGLContext>
#endif
#if QT_CONFIG(vulkan)
#include <QVulkanInstance>
#endif
Q_LOGGING_CATEGORY(lcTests, "qt.quick.tests")
@ -491,6 +497,9 @@ private slots:
void rendererInterface();
void rendererInterfaceWithRenderControl_data();
void rendererInterfaceWithRenderControl();
private:
QTouchDevice *touchDevice;
QTouchDevice *touchDeviceWithVelocity;
@ -3692,7 +3701,217 @@ void tst_qquickwindow::rendererInterface()
QVERIFY(ok[2]);
QVERIFY(ok[3]);
}
}
void tst_qquickwindow::rendererInterfaceWithRenderControl_data()
{
QTest::addColumn<QSGRendererInterface::GraphicsApi>("api");
#if QT_CONFIG(opengl)
QTest::newRow("OpenGL") << QSGRendererInterface::OpenGLRhi;
#endif
#if QT_CONFIG(vulkan)
QTest::newRow("Vulkan") << QSGRendererInterface::VulkanRhi;
#endif
#ifdef Q_OS_WIN
if (QOperatingSystemVersion::current() > QOperatingSystemVersion::Windows7)
QTest::newRow("D3D11") << QSGRendererInterface::Direct3D11Rhi;
#endif
#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
QTest::newRow("Metal") << QSGRendererInterface::MetalRhi;
#endif
}
void tst_qquickwindow::rendererInterfaceWithRenderControl()
{
QFETCH(QSGRendererInterface::GraphicsApi, api);
// no automatic QVulkanInstance when used with rendercontrol, must create our own
#if QT_CONFIG(vulkan)
QVulkanInstance inst;
if (api == QSGRendererInterface::VulkanRhi) {
if (!inst.create())
QSKIP("Skipping Vulkan-based test due to failing to create Vulkan instance");
}
#endif
{
// Changing the graphics api is not possible once a QQuickWindow et al is
// created, however we do support changing it once all QQuickWindow,
// QQuickRenderControl, etc. instances are destroyed, before creating new
// ones. That's why it is possible to have this test run with multiple QRhi
// backends.
QQuickWindow::setSceneGraphBackend(api);
QScopedPointer<QQuickRenderControl> renderControl(new QQuickRenderControl);
QScopedPointer<QQuickWindow> quickWindow(new QQuickWindow(renderControl.data()));
#if QT_CONFIG(vulkan)
if (api == QSGRendererInterface::VulkanRhi)
quickWindow->setVulkanInstance(&inst);
#endif
QScopedPointer<QQmlEngine> qmlEngine(new QQmlEngine);
// Pick a scene that does not have a Window, as having a Window in there is
// incompatible with QQuickRenderControl-based redirection by definition.
QScopedPointer<QQmlComponent> qmlComponent(new QQmlComponent(qmlEngine.data(),
testFileUrl(QLatin1String("showHideAnimate.qml"))));
QVERIFY(!qmlComponent->isLoading());
if (qmlComponent->isError()) {
for (const QQmlError &error : qmlComponent->errors())
qWarning() << error.url() << error.line() << error;
}
QVERIFY(!qmlComponent->isError());
QObject *rootObject = qmlComponent->create();
if (qmlComponent->isError()) {
for (const QQmlError &error : qmlComponent->errors())
qWarning() << error.url() << error.line() << error;
}
QVERIFY(!qmlComponent->isError());
QQuickItem *rootItem = qobject_cast<QQuickItem *>(rootObject);
QVERIFY(rootItem);
rootItem->setSize(QSize(200, 200));
quickWindow->contentItem()->setSize(rootItem->size());
quickWindow->setGeometry(0, 0, rootItem->width(), rootItem->height());
rootItem->setParentItem(quickWindow->contentItem());
const bool initSuccess = renderControl->initialize();
// We cannot just test for initSuccess; it is highly likely that a
// number of configurations will simply fail in a CI environment
// (Vulkan, Metal, ...) So the only reasonable choice is to skip if
// initialize() failed. The exception for now is OpenGL - that should
// usually work (except software backend etc.).
if (!initSuccess) {
#if QT_CONFIG(opengl)
if (api != QSGRendererInterface::OpenGLRhi
|| !QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::OpenGL))
#endif
{
QSKIP("Could not initialize graphics, perhaps unsupported graphics API, skipping");
}
}
// Not strictly required for the CI (because platforms like offscreen
// skip already above), but play nice with running with
// QT_QUICK_BACKEND=software on a normal desktop platform.
if (QQuickWindow::sceneGraphBackend() == QLatin1String("software"))
QSKIP("Software backend was forced via env.var, skipping this test");
QVERIFY(initSuccess);
QQuickWindow *window = quickWindow.data();
QSGRendererInterface *rif = window->rendererInterface();
QVERIFY(rif);
QCOMPARE(rif->graphicsApi(), api);
QRhi *rhi = static_cast<QRhi *>(rif->getResource(window, QSGRendererInterface::RhiResource));
QVERIFY(rhi);
// not an on-screen window so no swapchain
QVERIFY(!rif->getResource(window, QSGRendererInterface::RhiSwapchainResource));
switch (rif->graphicsApi()) {
case QSGRendererInterface::OpenGLRhi:
QVERIFY(rif->getResource(window, QSGRendererInterface::OpenGLContextResource));
#if QT_CONFIG(opengl)
{
QOpenGLContext *ctx = static_cast<QOpenGLContext *>(rif->getResource(window, QSGRendererInterface::OpenGLContextResource));
QVERIFY(ctx->isValid());
}
#endif
break;
case QSGRendererInterface::Direct3D11Rhi:
QVERIFY(rif->getResource(window, QSGRendererInterface::DeviceResource));
QVERIFY(rif->getResource(window, QSGRendererInterface::DeviceContextResource));
break;
case QSGRendererInterface::VulkanRhi:
QVERIFY(rif->getResource(window, QSGRendererInterface::DeviceResource));
QVERIFY(rif->getResource(window, QSGRendererInterface::PhysicalDeviceResource));
QVERIFY(rif->getResource(window, QSGRendererInterface::VulkanInstanceResource));
#if QT_CONFIG(vulkan)
QCOMPARE(rif->getResource(window, QSGRendererInterface::VulkanInstanceResource), window->vulkanInstance());
QCOMPARE(rif->getResource(window, QSGRendererInterface::VulkanInstanceResource), &inst);
#endif
QVERIFY(rif->getResource(window, QSGRendererInterface::CommandQueueResource));
break;
case QSGRendererInterface::MetalRhi:
QVERIFY(rif->getResource(window, QSGRendererInterface::DeviceResource));
QVERIFY(rif->getResource(window, QSGRendererInterface::CommandQueueResource));
break;
default:
break;
}
const QSize size(1280, 720);
QScopedPointer<QRhiTexture> tex(rhi->newTexture(QRhiTexture::RGBA8, size, 1,
QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource));
QVERIFY(tex->create());
QScopedPointer<QRhiRenderBuffer> ds(rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, size, 1));
QVERIFY(ds->create());
QRhiTextureRenderTargetDescription rtDesc(QRhiColorAttachment(tex.data()));
rtDesc.setDepthStencilBuffer(ds.data());
QScopedPointer<QRhiTextureRenderTarget> texRt(rhi->newTextureRenderTarget(rtDesc));
QScopedPointer<QRhiRenderPassDescriptor> rp(texRt->newCompatibleRenderPassDescriptor());
texRt->setRenderPassDescriptor(rp.data());
QVERIFY(texRt->create());
// redirect Qt Quick rendering into our texture
quickWindow->setRenderTarget(QQuickRenderTarget::fromRhiRenderTarget(texRt.data()));
bool ok[4] = { false, false, false, false };
auto f = [&ok, window](int idx) {
QSGRendererInterface *rif = window->rendererInterface();
if (rif) {
ok[idx] = true;
switch (rif->graphicsApi()) {
case QSGRendererInterface::VulkanRhi:
if (!rif->getResource(window, QSGRendererInterface::CommandListResource))
ok[idx] = false;
if (!rif->getResource(window, QSGRendererInterface::RenderPassResource))
ok[idx] = false;
break;
case QSGRendererInterface::MetalRhi:
if (!rif->getResource(window, QSGRendererInterface::CommandListResource))
ok[idx] = false;
if (!rif->getResource(window, QSGRendererInterface::CommandEncoderResource))
ok[idx] = false;
break;
default:
break;
}
}
};
// we could just check this below like the RhiRedirect* ones, but this way we test the signals as well
QObject::connect(window, &QQuickWindow::beforeRendering, window, std::bind(f, 0), Qt::DirectConnection);
QObject::connect(window, &QQuickWindow::beforeRenderPassRecording, window, std::bind(f, 1), Qt::DirectConnection);
QObject::connect(window, &QQuickWindow::afterRenderPassRecording, window, std::bind(f, 2), Qt::DirectConnection);
QObject::connect(window, &QQuickWindow::afterRendering, window, std::bind(f, 3), Qt::DirectConnection);
renderControl->polishItems();
renderControl->beginFrame();
renderControl->sync(); // this is when the custom rendertarget request gets processed
// now check for the queries that are used by Quick3D f.ex.
QVERIFY(rif->getResource(window, QSGRendererInterface::RhiRedirectCommandBuffer));
QVERIFY(rif->getResource(window, QSGRendererInterface::RhiRedirectRenderTarget));
QCOMPARE(rif->getResource(window, QSGRendererInterface::RhiRedirectRenderTarget), texRt.data());
renderControl->render();
renderControl->endFrame();
QVERIFY(ok[0]);
QVERIFY(ok[1]);
QVERIFY(ok[2]);
QVERIFY(ok[3]);
}
// Now that everything is torn down, go back to the default unspecified-api
// state, to prevent conflicting with tests that come afterwards.
QQuickWindow::setSceneGraphBackend(QSGRendererInterface::Unknown);
}
QTEST_MAIN(tst_qquickwindow)