blob: 4bab4e345a67c4bef398b3c0029305862efe4213 [file] [log] [blame]
/****************************************************************************
**
** 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 <qtest.h>
#if QT_CONFIG(opengl)
#include <QOffscreenSurface>
#include <QOpenGLContext>
#include <QOpenGLFunctions>
#include <QOpenGLFramebufferObject>
#endif
#include <QAnimationDriver>
#include <QQuickWindow>
#include <QQuickRenderControl>
#include <QQuickItem>
#include <QQmlEngine>
#include <QQmlComponent>
#include "../../shared/util.h"
#include <QtGui/private/qguiapplication_p.h>
#include <QtGui/qpa/qplatformintegration.h>
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<AnimationDriver> 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<QOpenGLContext> context(new QOpenGLContext);
QVERIFY(context->create());
QScopedPointer<QOffscreenSurface> offscreenSurface(new QOffscreenSurface);
offscreenSurface->setFormat(context->format());
offscreenSurface->create();
QVERIFY(context->makeCurrent(offscreenSurface.data()));
QScopedPointer<QQuickRenderControl> renderControl(new QQuickRenderControl);
QScopedPointer<QQuickWindow> quickWindow(new QQuickWindow(renderControl.data()));
QScopedPointer<QQmlEngine> qmlEngine(new QQmlEngine);
QScopedPointer<QQmlComponent> 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<QQuickItem *>(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<QOpenGLFramebufferObject> 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)