From 8a7e8190b7c2e3c8a7fa4c2139f34136422a5bbc Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Sun, 22 Mar 2020 00:02:40 +0100 Subject: Add autotest for QQuickRenderControl Let's celebrate the feature's 6 year anniversary with introducing an autotest for it. Also happens to be good sample code for generating image sequences from animated Quick scenes driven by a custom animation driver. Tied to (direct) OpenGL at the moment. The RHIified version will appear eventually as the work for introducing RHI support in QQuickRenderControl progresses. Task-number: QTBUG-78595 Change-Id: I7294ce986dca9968407ae738afe7ed2640ecd103 Reviewed-by: Qt CI Bot Reviewed-by: Andy Nichols --- tests/auto/quick/qquickrendercontrol/data/rect.qml | 47 +++++ .../qquickrendercontrol/qquickrendercontrol.pro | 12 ++ .../tst_qquickrendercontrol.cpp | 202 +++++++++++++++++++++ tests/auto/quick/quick.pro | 3 +- 4 files changed, 263 insertions(+), 1 deletion(-) create mode 100644 tests/auto/quick/qquickrendercontrol/data/rect.qml create mode 100644 tests/auto/quick/qquickrendercontrol/qquickrendercontrol.pro create mode 100644 tests/auto/quick/qquickrendercontrol/tst_qquickrendercontrol.cpp diff --git a/tests/auto/quick/qquickrendercontrol/data/rect.qml b/tests/auto/quick/qquickrendercontrol/data/rect.qml new file mode 100644 index 0000000000..5f58869fb5 --- /dev/null +++ b/tests/auto/quick/qquickrendercontrol/data/rect.qml @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.2 + +Rectangle { + width: 200 + height: 200 + color: "steelblue" + Rectangle { + id: r + width: 150 + height: 150 + anchors.centerIn: parent + color: "palegreen" + NumberAnimation on rotation { from: 0; to: 360; duration: 5000; loops: -1 } + } + Text { + text: "Hello World\n\nrotation=" + Math.round(r.rotation) + anchors.centerIn: parent + } +} diff --git a/tests/auto/quick/qquickrendercontrol/qquickrendercontrol.pro b/tests/auto/quick/qquickrendercontrol/qquickrendercontrol.pro new file mode 100644 index 0000000000..958abe316d --- /dev/null +++ b/tests/auto/quick/qquickrendercontrol/qquickrendercontrol.pro @@ -0,0 +1,12 @@ +CONFIG += testcase +TARGET = tst_qquickrendercontrol +SOURCES += tst_qquickrendercontrol.cpp + +include (../../shared/util.pri) + +macos:CONFIG -= app_bundle + +QT += core-private gui-private qml-private quick-private testlib opengl + +OTHER_FILES += \ + data/rect.qml diff --git a/tests/auto/quick/qquickrendercontrol/tst_qquickrendercontrol.cpp b/tests/auto/quick/qquickrendercontrol/tst_qquickrendercontrol.cpp new file mode 100644 index 0000000000..4bab4e345a --- /dev/null +++ b/tests/auto/quick/qquickrendercontrol/tst_qquickrendercontrol.cpp @@ -0,0 +1,202 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#if QT_CONFIG(opengl) +#include +#include +#include +#include +#endif +#include + +#include +#include +#include +#include +#include + +#include "../../shared/util.h" + +#include +#include + +class tst_RenderControl : public QQmlDataTest +{ + Q_OBJECT + +private slots: + void initTestCase(); + void renderAndReadBack(); +}; + +void tst_RenderControl::initTestCase() +{ + QQmlDataTest::initTestCase(); +} + +class AnimationDriver : public QAnimationDriver +{ +public: + AnimationDriver(int msPerStep) : m_step(msPerStep) { } + + void advance() override + { + m_elapsed += m_step; + advanceAnimation(); + } + + qint64 elapsed() const override + { + return m_elapsed; + } + +private: + int m_step; + qint64 m_elapsed = 0; +}; + + +void tst_RenderControl::renderAndReadBack() +{ + static const int ANIM_ADVANCE_PER_FRAME = 16; // milliseconds + QScopedPointer animDriver(new AnimationDriver(ANIM_ADVANCE_PER_FRAME)); + animDriver->install(); + + // ### Qt 6: migrate this to OpenGL-on-RHI +#if QT_CONFIG(opengl) + if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::OpenGL)) + QSKIP("Skipping due to platform not supporting OpenGL at run time"); + + QScopedPointer context(new QOpenGLContext); + QVERIFY(context->create()); + QScopedPointer offscreenSurface(new QOffscreenSurface); + offscreenSurface->setFormat(context->format()); + offscreenSurface->create(); + QVERIFY(context->makeCurrent(offscreenSurface.data())); + + QScopedPointer renderControl(new QQuickRenderControl); + QScopedPointer quickWindow(new QQuickWindow(renderControl.data())); + QScopedPointer qmlEngine(new QQmlEngine); + + QScopedPointer qmlComponent(new QQmlComponent(qmlEngine.data(), testFileUrl(QLatin1String("rect.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(rootObject); + QVERIFY(rootItem); + QCOMPARE(rootItem->size(), QSize(200, 200)); + + quickWindow->contentItem()->setSize(rootItem->size()); + quickWindow->setGeometry(0, 0, rootItem->width(), rootItem->height()); + + rootItem->setParentItem(quickWindow->contentItem()); + + renderControl->initialize(context.data()); + + // cannot do this test with the 'software' backend of Qt Quick + QSGRendererInterface::GraphicsApi api = quickWindow->rendererInterface()->graphicsApi(); + if (api != QSGRendererInterface::OpenGL) + QSKIP("Skipping due to Qt Quick not using the direct OpenGL rendering path"); + + QScopedPointer fbo(new QOpenGLFramebufferObject(rootItem->size().toSize(), + QOpenGLFramebufferObject::CombinedDepthStencil)); + + quickWindow->setRenderTarget(fbo.data()); + + for (int frame = 0; frame < 100; ++frame) { + // have to process events, e.g. to get queued metacalls delivered + QCoreApplication::processEvents(); + + if (frame > 0) { + // Quick animations will now think that ANIM_ADVANCE_PER_FRAME milliseconds have passed, + // even though in reality we have a tight loop that generates frames unthrottled. + animDriver->advance(); + } + + renderControl->polishItems(); + renderControl->sync(); + renderControl->render(); + + context->functions()->glFlush(); + + QImage img = fbo->toImage(); + QVERIFY(!img.isNull()); + QCOMPARE(img.size(), rootItem->size()); + + const int maxFuzz = 2; + + // The scene is: background, rectangle, text + // where rectangle rotates + + QRgb background = img.pixel(5, 5); + QVERIFY(qAbs(qRed(background) - 70) < maxFuzz); + QVERIFY(qAbs(qGreen(background) - 130) < maxFuzz); + QVERIFY(qAbs(qBlue(background) - 180) < maxFuzz); + + background = img.pixel(195, 195); + QVERIFY(qAbs(qRed(background) - 70) < maxFuzz); + QVERIFY(qAbs(qGreen(background) - 130) < maxFuzz); + QVERIFY(qAbs(qBlue(background) - 180) < maxFuzz); + + // after about 1.25 seconds (animation time, one iteration is 16 ms + // thanks to our custom animation driver) the rectangle reaches a 90 + // degree rotation, that should be frame 76 + if (frame <= 2 || (frame >= 76 && frame <= 80)) { + QRgb c = img.pixel(28, 28); // rectangle + QVERIFY(qAbs(qRed(c) - 152) < maxFuzz); + QVERIFY(qAbs(qGreen(c) - 251) < maxFuzz); + QVERIFY(qAbs(qBlue(c) - 152) < maxFuzz); + } else { + QRgb c = img.pixel(28, 28); // background because rectangle got rotated so this pixel is not covered by it + QVERIFY(qAbs(qRed(c) - 70) < maxFuzz); + QVERIFY(qAbs(qGreen(c) - 130) < maxFuzz); + QVERIFY(qAbs(qBlue(c) - 180) < maxFuzz); + } + } + +#else + QSKIP("No OpenGL, skipping rendercontrol test"); +#endif +} + +#include "tst_qquickrendercontrol.moc" + +QTEST_MAIN(tst_RenderControl) diff --git a/tests/auto/quick/quick.pro b/tests/auto/quick/quick.pro index 9ab7119903..0db8abd951 100644 --- a/tests/auto/quick/quick.pro +++ b/tests/auto/quick/quick.pro @@ -15,7 +15,8 @@ qtConfig(opengl(es1|es2)?) { qquickframebufferobject \ qquickopenglinfo \ qquickspritesequence \ - qquickshadereffect + qquickshadereffect \ + qquickrendercontrol } !cross_compile: PRIVATETESTS += examples -- cgit v1.2.3