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:
parent
fdba8facd0
commit
38bd2bd831
|
@ -5592,7 +5592,7 @@ void QQuickWindow::setSceneGraphBackend(QSGRendererInterface::GraphicsApi api)
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (QSGRendererInterface::isApiRhiBased(api))
|
if (QSGRendererInterface::isApiRhiBased(api) || api == QSGRendererInterface::Unknown)
|
||||||
QSGRhiSupport::configure(api);
|
QSGRhiSupport::configure(api);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -268,11 +268,18 @@ void QSGRhiSupport::checkEnvQSgInfo()
|
||||||
|
|
||||||
void QSGRhiSupport::configure(QSGRendererInterface::GraphicsApi api)
|
void QSGRhiSupport::configure(QSGRendererInterface::GraphicsApi api)
|
||||||
{
|
{
|
||||||
Q_ASSERT(QSGRendererInterface::isApiRhiBased(api));
|
if (api == QSGRendererInterface::Unknown) {
|
||||||
QSGRhiSupport *inst = staticInst();
|
// behave as if nothing was explicitly requested
|
||||||
inst->m_requested.valid = true;
|
QSGRhiSupport *inst = staticInst();
|
||||||
inst->m_requested.api = api;
|
inst->m_requested.valid = false;
|
||||||
inst->applySettings();
|
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()
|
QSGRhiSupport *QSGRhiSupport::instance()
|
||||||
|
|
|
@ -44,13 +44,19 @@
|
||||||
#include <QSignalSpy>
|
#include <QSignalSpy>
|
||||||
#include <private/qquickwindow_p.h>
|
#include <private/qquickwindow_p.h>
|
||||||
#include <private/qguiapplication_p.h>
|
#include <private/qguiapplication_p.h>
|
||||||
|
#include <QtGui/qpa/qplatformintegration.h>
|
||||||
#include <QRunnable>
|
#include <QRunnable>
|
||||||
#include <QSGRendererInterface>
|
#include <QSGRendererInterface>
|
||||||
#include <QQuickRenderControl>
|
#include <QQuickRenderControl>
|
||||||
|
#include <QOperatingSystemVersion>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <QtGui/private/qrhi_p.h>
|
||||||
#if QT_CONFIG(opengl)
|
#if QT_CONFIG(opengl)
|
||||||
#include <QOpenGLContext>
|
#include <QOpenGLContext>
|
||||||
#endif
|
#endif
|
||||||
|
#if QT_CONFIG(vulkan)
|
||||||
|
#include <QVulkanInstance>
|
||||||
|
#endif
|
||||||
|
|
||||||
Q_LOGGING_CATEGORY(lcTests, "qt.quick.tests")
|
Q_LOGGING_CATEGORY(lcTests, "qt.quick.tests")
|
||||||
|
|
||||||
|
@ -491,6 +497,9 @@ private slots:
|
||||||
|
|
||||||
void rendererInterface();
|
void rendererInterface();
|
||||||
|
|
||||||
|
void rendererInterfaceWithRenderControl_data();
|
||||||
|
void rendererInterfaceWithRenderControl();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QTouchDevice *touchDevice;
|
QTouchDevice *touchDevice;
|
||||||
QTouchDevice *touchDeviceWithVelocity;
|
QTouchDevice *touchDeviceWithVelocity;
|
||||||
|
@ -3692,7 +3701,217 @@ void tst_qquickwindow::rendererInterface()
|
||||||
QVERIFY(ok[2]);
|
QVERIFY(ok[2]);
|
||||||
QVERIFY(ok[3]);
|
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)
|
QTEST_MAIN(tst_qquickwindow)
|
||||||
|
|
Loading…
Reference in New Issue