Revision control

Copy as Markdown

// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QTest>
#include <QSignalSpy>
#include <QQuickView>
#include <QQmlEngine>
#include <QQmlComponent>
#include <rhi/qrhi.h>
#include "../shared/util.h"
class tst_MultiWindow : public QQuick3DDataTest
{
Q_OBJECT
private slots:
void initTestCase() override;
void separateCubeSceneInMultipleWindows_data();
void separateCubeSceneInMultipleWindows();
void cubeMultiViewportSameWindow();
void cubeMultiViewportMultiWindow();
void cubeMultiWindow();
};
void tst_MultiWindow::initTestCase()
{
QQuick3DDataTest::initTestCase();
if (!initialized())
return;
}
const int FUZZ = 5;
void tst_MultiWindow::separateCubeSceneInMultipleWindows_data()
{
QTest::addColumn<int>("viewCount");
QTest::newRow("2 windows") << 2;
QTest::newRow("3 windows") << 3;
QTest::newRow("4 windows") << 4;
}
// Open 2-4 windows, each with an independent source QML scene.
void tst_MultiWindow::separateCubeSceneInMultipleWindows()
{
QFETCH(int, viewCount);
QVector<QQuickView *> views;
views.resize(viewCount);
for (int i = 0; i < viewCount; ++i) {
views[i] = createView(QLatin1String("texturedcube.qml"), QSize(640, 480));
views[i]->setGeometry(i * 100, i * 100, 640, 480);
QVERIFY(views[i]);
}
for (int i = 0; i < viewCount; ++i)
QVERIFY(QTest::qWaitForWindowExposed(views[i]));
for (int i = 0; i < viewCount; ++i) {
const QImage result = grab(views[i]);
if (result.isNull())
return;
QVERIFY(comparePixelNormPos(result, 0.5, 0.5, QColor::fromRgb(59, 192, 77), FUZZ));
}
qDeleteAll(views);
}
// Have a single window with 4 View3Ds rendering the same scene via importScene.
// Then change the (shared) Texture's source to be a QQuickItem.
void tst_MultiWindow::cubeMultiViewportSameWindow()
{
QScopedPointer<QQuickView> view(createView(QLatin1String("texturedcube_multiviewport.qml"), QSize(640, 480)));
QVERIFY(view);
QVERIFY(QTest::qWaitForWindowExposed(view.data()));
QImage result = grab(view.data());
if (result.isNull())
return;
const qreal dpr = view->devicePixelRatio();
// First the cube is textured with a Qt logo.
QVERIFY(comparePixel(result, 25, 25, dpr, QColor::fromRgb(0, 0, 0), FUZZ));
QVERIFY(comparePixel(result, 160, 128, dpr, QColor::fromRgb(59, 192, 77), FUZZ));
QVERIFY(comparePixel(result, 25 + 320, 25, dpr, QColor::fromRgb(255, 0, 0), FUZZ));
QVERIFY(comparePixel(result, 160 + 320, 128, dpr, QColor::fromRgb(59, 192, 77), FUZZ));
QVERIFY(comparePixel(result, 25, 25 + 240, dpr, QColor::fromRgb(0, 128, 0), FUZZ));
QVERIFY(comparePixel(result, 160, 128 + 240, dpr, QColor::fromRgb(59, 192, 77), FUZZ));
QVERIFY(comparePixel(result, 25 + 320, 25 + 240, dpr, QColor::fromRgb(0, 0, 255), FUZZ));
QVERIFY(comparePixel(result, 160 + 320, 128 + 240, dpr, QColor::fromRgb(59, 192, 77), FUZZ));
QMetaObject::invokeMethod(view->rootObject(), "changeToSourceItemBasedTexture");
result = grab(view.data());
if (result.isNull())
return;
// Now the texture is a filled red rectangle.
QVERIFY(comparePixel(result, 25, 25, dpr, QColor::fromRgb(0, 0, 0), FUZZ));
QVERIFY(comparePixel(result, 160, 128, dpr, QColor::fromRgb(239, 0, 0), FUZZ));
QVERIFY(comparePixel(result, 25 + 320, 25, dpr, QColor::fromRgb(255, 0, 0), FUZZ));
QVERIFY(comparePixel(result, 160 + 320, 128, dpr, QColor::fromRgb(239, 0, 0), FUZZ));
QVERIFY(comparePixel(result, 25, 25 + 240, dpr, QColor::fromRgb(0, 128, 0), FUZZ));
QVERIFY(comparePixel(result, 160, 128 + 240, dpr, QColor::fromRgb(239, 0, 0), FUZZ));
QVERIFY(comparePixel(result, 25 + 320, 25 + 240, dpr, QColor::fromRgb(0, 0, 255), FUZZ));
QVERIFY(comparePixel(result, 160 + 320, 128 + 240, dpr, QColor::fromRgb(239, 0, 0), FUZZ));
}
// Two View3Ds in two separate windows, importing the same scene.
void tst_MultiWindow::cubeMultiViewportMultiWindow()
{
QQmlEngine engine;
QQmlComponent component(&engine);
component.loadUrl(testFileUrl("texturedcube_multiviewport_multiwindow.qml"));
QObject *obj = component.create();
QScopedPointer<QObject> cleanup(obj);
QVERIFY(obj);
QQuickWindow *window1 = qobject_cast<QQuickWindow*>(obj);
QVERIFY(window1);
QQuickWindow *window2 = window1->findChild<QQuickWindow *>("window2");
QVERIFY(window2);
QVERIFY(QTest::qWaitForWindowExposed(window1));
QVERIFY(QTest::qWaitForWindowExposed(window2));
QImage result = grab(window1);
if (result.isNull())
return;
QVERIFY(comparePixelNormPos(result, 0.5, 0.5, QColor::fromRgb(59, 192, 77), FUZZ));
result = grab(window2);
if (result.isNull())
return;
QVERIFY(comparePixelNormPos(result, 0.5, 0.5, QColor::fromRgb(59, 192, 77), FUZZ));
{
QQuick3DTestMessageHandler msgCatcher;
QSignalSpy sceneUpdate1(window1, &QQuickWindow::afterFrameEnd);
QSignalSpy sceneUpdate2(window2, &QQuickWindow::afterFrameEnd);
QMetaObject::invokeMethod(window1, "changeToSourceItemBasedTexture");
bool b = QTest::qWaitFor([&](){ return sceneUpdate1.size() > 1; });
result = grab(window1);
if (!b || result.isNull()) {
qWarning("Test skipped!");
return;
}
QVERIFY(comparePixelNormPos(result, 0.5, 0.5, QColor::fromRgb(239, 0, 0), FUZZ));
b = QTest::qWaitFor([&](){ return sceneUpdate2.size() > 1; });
result = grab(window2);
if (!b || result.isNull()) {
qWarning("Test skipped!");
return;
}
QRhi *rhi1 = static_cast<QRhi *>(window1->rendererInterface()->getResource(window1, QSGRendererInterface::RhiResource));
QVERIFY(rhi1);
QRhi *rhi2 = static_cast<QRhi *>(window2->rendererInterface()->getResource(window2, QSGRendererInterface::RhiResource));
QVERIFY(rhi2);
if (rhi1 != rhi2) {
QVERIFY(!msgCatcher.messageString().contains(QLatin1String("Cannot use QSGTexture")));
} else {
QVERIFY(comparePixelNormPos(result, 0.5, 0.5, QColor::fromRgb(239, 0, 0), FUZZ));
}
}
}
// One View3D moved between two separate windows
void tst_MultiWindow::cubeMultiWindow()
{
QQmlEngine engine;
QQmlComponent component(&engine);
component.loadUrl(testFileUrl("texturedcube_multiwindow.qml"));
QObject *obj = component.create();
QScopedPointer<QObject> cleanup(obj);
QVERIFY(obj);
QQuickWindow *window1 = qobject_cast<QQuickWindow *>(obj);
QVERIFY(window1);
QQuickWindow *window2 = window1->findChild<QQuickWindow *>("window2");
QVERIFY(window2);
QVERIFY(QTest::qWaitForWindowExposed(window1));
QVERIFY(QTest::qWaitForWindowExposed(window2));
QImage result = grab(window1);
if (result.isNull())
return;
// Check first window OK
QVERIFY(comparePixelNormPos(result, 0.5, 0.5, QColor::fromRgb(59, 192, 77), FUZZ));
QMetaObject::invokeMethod(window1, "reparentView");
result = grab(window2);
if (result.isNull())
return;
// Check scene moved to second window
QVERIFY(comparePixelNormPos(result, 0.5, 0.5, QColor::fromRgb(59, 192, 77), FUZZ));
result = grab(window1);
if (result.isNull())
return;
// Check first window empty
QVERIFY(comparePixelNormPos(result, 0.5, 0.5, QColor::fromRgb(255, 255, 255), FUZZ));
}
QTEST_MAIN(tst_MultiWindow)
#include "tst_multiwindow.moc"